Optimize Phoenix Component caching
This commit is contained in:
@ -12,69 +12,42 @@ defmodule Heroicons.Cache do
|
|||||||
@doc false
|
@doc false
|
||||||
def start_link(_), do: GenServer.start_link(__MODULE__, [], name: @name)
|
def start_link(_), do: GenServer.start_link(__MODULE__, [], name: @name)
|
||||||
|
|
||||||
@doc "Fetch a pre-compiled Phoenix Component from the cache or disk, given a `path`"
|
@doc "Fetch a icon's body and fingerprint from the cache or disk, given a `path`"
|
||||||
def fetch_component(path) do
|
def fetch_icon(path) do
|
||||||
case :ets.lookup(@name.Components, path) do
|
|
||||||
[{^path, component}] ->
|
|
||||||
component
|
|
||||||
|
|
||||||
[] ->
|
|
||||||
GenServer.call(@name, {:cache_component, path})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Fetch a icon's body from the cache or disk, given a `path`"
|
|
||||||
def fetch_body(path) do
|
|
||||||
case :ets.lookup(@name, path) do
|
case :ets.lookup(@name, path) do
|
||||||
[{^path, body}] ->
|
[{^path, icon_body, fingerprint}] ->
|
||||||
body
|
{icon_body, fingerprint}
|
||||||
|
|
||||||
[] ->
|
[] ->
|
||||||
GenServer.call(@name, {:cache_body, path})
|
GenServer.call(@name, {:cache_icon, path})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(_) do
|
def init(_) do
|
||||||
:ets.new(@name, [:set, :protected, :named_table])
|
:ets.new(@name, [:set, :protected, :named_table])
|
||||||
:ets.new(@name.Components, [:set, :protected, :named_table])
|
|
||||||
|
|
||||||
{:ok, []}
|
{:ok, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_call({:cache_body, path}, _ref, state) do
|
def handle_call({:cache_icon, path}, _ref, state) do
|
||||||
body = read_body(path)
|
{icon_body, fingerprint} = read_icon(path)
|
||||||
|
|
||||||
:ets.insert_new(@name, {path, body})
|
:ets.insert_new(@name, {path, icon_body, fingerprint})
|
||||||
|
|
||||||
{:reply, body, state}
|
{:reply, {icon_body, fingerprint}, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:cache_component, path}, _ref, state) do
|
defp read_icon(path) do
|
||||||
body = read_body(path)
|
|
||||||
|
|
||||||
component =
|
|
||||||
EEx.compile_string("<svg {@attrs}" <> body,
|
|
||||||
engine: Phoenix.LiveView.HTMLEngine,
|
|
||||||
file: __ENV__.file,
|
|
||||||
line: __ENV__.line + 1,
|
|
||||||
module: __ENV__.module,
|
|
||||||
indentation: 0
|
|
||||||
)
|
|
||||||
|
|
||||||
:ets.insert_new(@name.Components, {path, component})
|
|
||||||
|
|
||||||
{:reply, component, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp read_body(path) do
|
|
||||||
icon =
|
icon =
|
||||||
Path.join(:code.priv_dir(:heroicons), path)
|
Path.join(:code.priv_dir(:heroicons), path)
|
||||||
|> File.read!()
|
|> File.read!()
|
||||||
|
|
||||||
<<"<svg ", body::binary>> = icon
|
<<"<svg", icon_body::binary>> = icon
|
||||||
|
|
||||||
body
|
<<fingerprint::8*16>> = :erlang.md5(icon)
|
||||||
|
|
||||||
|
{icon_body, fingerprint}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -71,13 +71,33 @@ defmodule Heroicons.Generator do
|
|||||||
attrs = @assigns_to_attrs_mod.assigns_to_attributes(assigns)
|
attrs = @assigns_to_attrs_mod.assigns_to_attributes(assigns)
|
||||||
assigns = @assign_mod.assign(assigns, :attrs, attrs)
|
assigns = @assign_mod.assign(assigns, :attrs, attrs)
|
||||||
|
|
||||||
{component, _binding} =
|
dynamic = fn track_changes? ->
|
||||||
Code.eval_quoted(
|
changed =
|
||||||
Heroicons.Cache.fetch_component(path),
|
case assigns do
|
||||||
assigns: assigns
|
%{__changed__: changed} when track_changes? -> changed
|
||||||
)
|
_ -> nil
|
||||||
|
end
|
||||||
|
|
||||||
component
|
attrs =
|
||||||
|
case Phoenix.LiveView.Engine.changed_assign?(changed, :attrs) do
|
||||||
|
true -> elem(Phoenix.HTML.attributes_escape(assigns.attrs), 1)
|
||||||
|
false -> nil
|
||||||
|
end
|
||||||
|
|
||||||
|
[attrs]
|
||||||
|
end
|
||||||
|
|
||||||
|
{icon_body, fingerprint} = Heroicons.Cache.fetch_icon(path)
|
||||||
|
|
||||||
|
%Phoenix.LiveView.Rendered{
|
||||||
|
static: [
|
||||||
|
"<svg",
|
||||||
|
icon_body
|
||||||
|
],
|
||||||
|
dynamic: dynamic,
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
root: true
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
@ -92,12 +112,13 @@ defmodule Heroicons.Generator do
|
|||||||
{:safe, [?\s, safe_k, ?=, ?", safe_v, ?"]}
|
{:safe, [?\s, safe_k, ?=, ?", safe_v, ?"]}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{icon_body, _fingerprint} = Heroicons.Cache.fetch_icon(path)
|
||||||
|
|
||||||
{:safe,
|
{:safe,
|
||||||
[
|
[
|
||||||
"<svg",
|
"<svg",
|
||||||
Phoenix.HTML.Safe.to_iodata(attrs),
|
Phoenix.HTML.Safe.to_iodata(attrs),
|
||||||
" ",
|
icon_body
|
||||||
Heroicons.Cache.fetch_body(path)
|
|
||||||
]}
|
]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user