查看源代码 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 包含一个函数,该函数接受一个布尔值参数(见下文“跟踪更改”),并返回一个动态内容列表。列表中的每个元素都是以下之一:

  1. iodata - 它是动态内容
  2. nil - 动态内容未发生更改
  3. 另一个 Phoenix.LiveView.Rendered 结构,见下文“嵌套和指纹”
  4. 一个 Phoenix.LiveView.Comprehension 结构,见下文“推导式”
  5. 一个 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 算法延迟处理。