查看源代码 Phoenix.LiveView.Engine (Phoenix LiveView v0.20.17)
一个跟踪更改的 EEx
模板引擎。
这通常由 Phoenix.LiveView.TagEngine
使用,它还添加了 HTML 验证。在下面的文档中,我们将解释它是如何从内部工作的。有关面向用户的文档,请参阅 Phoenix.LiveView
。
Phoenix.LiveView.Rendered
每当您渲染一个实时模板时,它都会返回一个 Phoenix.LiveView.Rendered
结构。此结构具有三个字段::static
、:dynamic
和 :fingerprint
。
字段 :static
是一个文字字符串列表。这允许 Elixir 编译器优化此列表并避免在每次渲染时分配其字符串。
字段 :dynamic
包含一个函数,该函数接受一个布尔值参数(见下文“跟踪更改”),并返回一个动态内容列表。列表中的每个元素都是以下之一:
- iodata - 它是动态内容
- nil - 动态内容未发生更改
- 另一个
Phoenix.LiveView.Rendered
结构,见下文“嵌套和指纹” - 一个
Phoenix.LiveView.Comprehension
结构,见下文“推导式” - 一个
Phoenix.LiveView.Component
结构,见下文“组件”
当您渲染一个实时模板时,您可以通过交替使用静态和动态字段将渲染后的结构转换为 iodata,始终以一个静态条目开头,然后是一个动态条目。最后一个条目也总是静态的。因此,以下结构
%Phoenix.LiveView.Rendered{
static: ["foo", "bar", "baz"],
dynamic: fn track_changes? -> ["left", "right"] end
}
会导致以下内容作为 iodata 发送到网络
["foo", "left", "bar", "right", "baz"]
这也是使用 Phoenix.HTML.Safe.to_iodata/1
和一个 Phoenix.LiveView.Rendered
结构返回的结果。
当然,实时模板的好处正是您不必每次都发送静态和动态段。所以让我们谈谈跟踪更改。
跟踪更改
默认情况下,实时模板不跟踪更改。可以通过在 assigns 中包含一个更改的映射,其键为 __changed__
,并将 true
传递给动态部分来启用更改跟踪。该映射应包含任何更改的字段的名称作为键,布尔值 true 作为值。如果一个字段没有列在 __changed__
中,则它始终被认为是未更改的。
如果一个字段没有更改,并且 live 认为动态表达式不再需要计算,则它在 dynamic
列表中的值将为 nil
。此信息可用于避免将数据发送到客户端。
嵌套和指纹
Phoenix.LiveView
还跟踪跨实时模板的更改。因此,如果您的视图包含以下内容
<%= render "form.html", assigns %>
Phoenix 将能够跟踪跨模板的静态和动态内容,以及哪些内容发生了更改。一个渲染后的嵌套 live
模板将作为另一个 Phoenix.LiveView.Rendered
结构出现在 dynamic
列表中,该结构必须递归处理。
但是,由于实时模板的渲染本身可以是动态的,因此区分渲染了哪个实时模板很重要。例如,想象一下这段代码
<%= if something?, do: render("one.html", assigns), else: render("other.html", assigns) %>
为了解决这个问题,所有 Phoenix.LiveView.Rendered
结构也包含一个指纹字段,该字段唯一地标识它。如果指纹相等,则您拥有相同的模板,因此可以只传输其更改。
推导式
实时模板进行的另一种优化是跟踪推导式。如果您的代码包含以下内容
<%= for point <- @points do %>
x: <%= point.x %>
y: <%= point.y %>
<% end %>
它不会渲染所有点及其静态和动态部分,而是返回一个 Phoenix.LiveView.Comprehension
结构,该结构包含在所有点之间共享的静态部分,以及要在静态部分内插的动态列表。如果 @points
是一个包含 %{x: 1, y: 2}
和 %{x: 3, y: 4}
的列表,则上述表达式将返回
%Phoenix.LiveView.Comprehension{
static: ["\n x: ", "\n y: ", "\n"],
dynamics: [
["1", "2"],
["3", "4"]
]
}
这允许实时模板显着优化推导式发送的数据,因为静态部分只发射一次,与项目的数量无关。
动态列表始终是 iodatas 或组件的列表,因为我们不会在推导式本身内执行更改跟踪。同样,推导式没有指纹,因为它们只在根部进行优化,因此条件评估(如渲染中看到的)是不可能的。返回推导式的动态字段的唯一可能结果是 nil
。
组件
Live 还支持使用 Phoenix.LiveComponent
定义的有状态组件。由于它们是有状态的,因此它们总是被 diff 算法延迟处理。