Skip to content

Commit 92ce429

Browse files
josevalimSteffenDE
andauthored
Simplify emitted code when there are no dynamics (#3902)
* Simplify emitted code when there are no dynamics * Update lib/phoenix_live_view/tag_engine.ex * Update lib/phoenix_live_view/engine.ex * add test for slot optimization --------- Co-authored-by: Steffen Deusch <[email protected]>
1 parent 677e138 commit 92ce429

File tree

4 files changed

+90
-20
lines changed

4 files changed

+90
-20
lines changed

lib/phoenix_component.ex

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,10 @@ defmodule Phoenix.Component do
11501150
end
11511151

11521152
def __render_slot__(changed, entry, argument) when is_map(entry) do
1153-
entry.inner_block.(changed, argument)
1153+
case entry.inner_block do
1154+
%Phoenix.LiveView.Rendered{} = rendered -> rendered
1155+
fun -> fun.(changed, argument)
1156+
end
11541157
end
11551158

11561159
defp call_inner_block!(entry, changed, argument) do
@@ -1159,7 +1162,10 @@ defmodule Phoenix.Component do
11591162
raise RuntimeError, message
11601163
end
11611164

1162-
entry.inner_block.(changed, argument)
1165+
case entry.inner_block do
1166+
%Phoenix.LiveView.Rendered{} = rendered -> rendered
1167+
fun -> fun.(changed, argument)
1168+
end
11631169
end
11641170

11651171
@doc """

lib/phoenix_live_view/engine.ex

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -399,14 +399,28 @@ defmodule Phoenix.LiveView.Engine do
399399

400400
root = Keyword.get(opts, :root, meta[:root])
401401

402+
dynamic =
403+
if dynamic == [] do
404+
quote do
405+
fn _ ->
406+
_ = unquote(@assigns_var)
407+
[]
408+
end
409+
end
410+
else
411+
quote do
412+
fn track_changes? ->
413+
changed = unquote(changed)
414+
vars_changed = if track_changes?, do: vars_changed
415+
unquote({:__block__, [], block})
416+
unquote(dynamic)
417+
end
418+
end
419+
end
420+
402421
{:ok,
403422
quote do
404-
dynamic = fn track_changes? ->
405-
changed = unquote(changed)
406-
vars_changed = if track_changes?, do: vars_changed
407-
unquote({:__block__, [], block})
408-
unquote(dynamic)
409-
end
423+
dynamic = unquote(dynamic)
410424

411425
%Phoenix.LiveView.Rendered{
412426
static: unquote(static),
@@ -898,7 +912,26 @@ defmodule Phoenix.LiveView.Engine do
898912
) do
899913
with {call, meta, [^key, [do: block]]} <- inner_block,
900914
:inner_block <- extract_call(call) do
901-
inner_block = {call, meta, [key, [do: maybe_block_to_rendered(block, vars, caller)]]}
915+
inner_block =
916+
case block do
917+
[
918+
{:->, _,
919+
[
920+
[{:_, _, _}],
921+
{:__block__, [live_rendered: true] ++ _meta, [{:safe, _}]} = static_block
922+
]}
923+
] ->
924+
# This is an optimization that removes the extra anonymous function from
925+
# the TagEngine's build_component_clauses function used for slots.
926+
# We can do this when the slot does not contain any dynamics, which also helps
927+
# to prevent uncovered lines when rendering a component with only a named slot.
928+
# In that case, the component still has an inner_block, but it only consists of
929+
# whitespace.
930+
maybe_block_to_rendered(static_block, vars, caller)
931+
932+
_ ->
933+
{call, meta, [key, [do: maybe_block_to_rendered(block, vars, caller)]]}
934+
end
902935

903936
{:%{}, map_meta, [__slot__: key, inner_block: inner_block] ++ attrs}
904937
else
@@ -1320,17 +1353,6 @@ defmodule Phoenix.LiveView.Engine do
13201353
quote line: line, do: Phoenix.HTML.Safe.List.to_iodata(unquote(literal))
13211354
end
13221355

1323-
# Calls to attributes escape is always safe
1324-
defp to_safe(
1325-
{{:., _, [Phoenix.LiveView.TagEngine, :attributes_escape]}, _, [_]} = safe,
1326-
line,
1327-
_extra_clauses?
1328-
) do
1329-
quote line: line do
1330-
elem(unquote(safe), 1)
1331-
end
1332-
end
1333-
13341356
defp to_safe(expr, line, false) do
13351357
quote line: line, do: unquote(__MODULE__).safe_to_iodata(unquote(expr))
13361358
end

lib/phoenix_live_view/tag_engine.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ defmodule Phoenix.LiveView.TagEngine do
140140
for more information.
141141
"""
142142
defmacro inner_block(name, do: do_block) do
143+
# TODO: Remove the catch-all clause, it is no longer used
143144
case do_block do
144145
[{:->, meta, _} | _] ->
145146
inner_fun = {:fn, meta, do_block}

test/phoenix_live_view/engine_test.exs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,47 @@ defmodule Phoenix.LiveView.EngineTest do
948948
end
949949
end
950950

951+
describe "slots" do
952+
import Phoenix.Component, only: [sigil_H: 2]
953+
954+
defp component(assigns) do
955+
%{inner_block: [%{inner_block: slot}]} = assigns
956+
throw(slot)
957+
end
958+
959+
test "slots with no dynamics represented as rendered struct" do
960+
try do
961+
assigns = %{}
962+
963+
%Phoenix.LiveView.Rendered{dynamic: dynamic} =
964+
~H"<.component>No dynamics</.component>"
965+
966+
dynamic.(true)
967+
catch
968+
slot ->
969+
assert %Phoenix.LiveView.Rendered{} = slot
970+
else
971+
_ -> flunk("Should have caught")
972+
end
973+
end
974+
975+
test "slots with dynamics are represented as function" do
976+
try do
977+
assigns = %{}
978+
979+
%Phoenix.LiveView.Rendered{dynamic: dynamic} =
980+
~H"<.component>{1234}</.component>"
981+
982+
dynamic.(true)
983+
catch
984+
slot ->
985+
assert is_function(slot)
986+
else
987+
_ -> flunk("Should have caught")
988+
end
989+
end
990+
end
991+
951992
defp eval(string, assigns \\ %{}, binding \\ []) do
952993
EEx.eval_string(string, [assigns: assigns] ++ binding, file: __ENV__.file, engine: Engine)
953994
end

0 commit comments

Comments
 (0)