查看源代码 欢迎
欢迎来到 Phoenix LiveView 文档。Phoenix LiveView 能够使用服务器渲染的 HTML 创建丰富的实时用户体验。有关 LiveView 及其优势的概述,请参见 我们的 README。
什么是 LiveView?
LiveView 是接收事件、更新其状态并以差异形式将更新渲染到页面的进程。
LiveView 编程模型是声明式的:它不是说“事件 X 发生后,在页面上更改 Y”,LiveView 中的事件是可能导致状态更改的常规消息。状态发生变化后,LiveView 将重新渲染其 HTML 模板的相关部分并将其推送到浏览器,浏览器将以最有效的方式更新页面。
LiveView 状态不过是一些函数式且不可变的 Elixir 数据结构。事件要么是内部应用程序消息(通常由 Phoenix.PubSub
发出),要么是客户端/浏览器发送的。
每个 LiveView 首先作为常规 HTTP 请求的一部分以静态方式渲染,这可以为“首次有意义的绘制”提供更快的响应时间,此外还可以帮助搜索和索引引擎。然后在客户端和服务器之间建立一个持久连接。这使 LiveView 应用程序能够更快地响应用户事件,因为与无状态请求相比,需要完成的工作更少,需要发送的数据更少,无状态请求必须在每次请求时进行身份验证、解码、加载和编码数据。
示例
LiveView 默认情况下包含在 Phoenix 应用程序中。因此,要使用 LiveView,您必须已经安装 Phoenix 并创建了您的第一个应用程序。如果您尚未完成此操作,请查看 Phoenix 的安装指南 以开始使用。
LiveView 的行为由一个模块来概述,该模块实现一系列函数作为回调。让我们看一个示例。将下面的文件写入 lib/my_app_web/live/thermostat_live.ex
defmodule MyAppWeb.ThermostatLive do
# In Phoenix v1.6+ apps, the line is typically: use MyAppWeb, :live_view
use Phoenix.LiveView
def render(assigns) do
~H"""
Current temperature: <%= @temperature %>°F
<button phx-click="inc_temperature">+</button>
"""
end
def mount(_params, _session, socket) do
temperature = 70 # Let's assume a fixed temperature for now
{:ok, assign(socket, :temperature, temperature)}
end
def handle_event("inc_temperature", _params, socket) do
{:noreply, update(socket, :temperature, &(&1 + 1))}
end
end
上面的模块定义了三个函数(它们是 LiveView 要求的回调)。第一个是 render/1
,它接收 socket assigns
,负责返回要在页面上渲染的内容。我们使用 ~H
符号来定义 HEEx 模板,它代表 HTML+EEx。它们是 Elixir 内置 EEx 模板的扩展,支持 HTML 验证、基于语法的组件、智能更改跟踪等。您可以在 Phoenix.Component.sigil_H/2
中了解有关模板语法的更多信息(请注意,当您使用 Phoenix.LiveView
时,会自动导入 Phoenix.Component
)。
渲染时使用的数据来自 mount
回调。当 LiveView 启动时,将调用 mount
回调。在其中,您可以访问请求参数、读取存储在会话中的信息(通常是识别当前用户的身份的信息)以及一个 socket。socket 是我们保存所有状态的地方,包括分配。 mount
继续将默认温度分配给 socket。由于 Elixir 数据结构是不可变的,因此 LiveView API 通常会接收 socket 并返回一个更新后的 socket。然后我们返回 {:ok, socket}
来表示我们能够成功安装 LiveView。在 mount
之后,LiveView 将使用来自 assigns
的值渲染页面并将其发送到客户端。
如果您查看渲染的 HTML,您会注意到有一个带有 phx-click
属性的按钮。单击该按钮时,一个 "inc_temperature"
事件将发送到服务器,该事件将匹配并由 handle_event
回调处理。此回调更新 socket 并返回带有更新后的 socket 的 {:noreply, socket}
。LiveView 中(以及 Elixir 中的一般情况)的 handle_*
回调是根据某些操作来调用的,在本例中,用户单击了一个按钮。{:noreply, socket}
返回表示没有其他回复发送到浏览器,只是渲染了页面的新版本。LiveView 然后计算差异并将其发送到客户端。
现在我们已准备好渲染 LiveView。您可以直接从路由器提供 LiveView
defmodule MyAppWeb.Router do
use Phoenix.Router
import Phoenix.LiveView.Router
scope "/", MyAppWeb do
live "/thermostat", ThermostatLive
end
end
渲染 LiveView 后,将发送常规 HTML 响应。在您的 app.js 文件中,您应该找到以下内容
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
liveSocket.connect()
现在 JavaScript 客户端将通过 WebSocket 连接,并且 mount/3
将在生成的 LiveView 进程中调用。
参数和会话
mount 回调接收三个参数:请求参数、会话和 socket。
参数可用于从 URL 读取信息。例如,假设您在某个地方定义了一个 Thermostat
模块,它可以根据房屋名称读取此信息,您可以这样写
def mount(%{"house" => house}, _session, socket) do
temperature = Thermostat.get_house_reading(house)
{:ok, assign(socket, :temperature, temperature)}
end
然后在您的路由器中
live "/thermostat/:house", ThermostatLive
会话从签名(或加密)的 cookie 中检索信息。这是您可以存储身份验证信息的地方,例如 current_user_id
def mount(_params, %{"current_user_id" => user_id}, socket) do
temperature = Thermostat.get_user_reading(user_id)
{:ok, assign(socket, :temperature, temperature)}
end
Phoenix 带有内置的身份验证生成器。参见
mix phx.gen.auth
。
大多数情况下,在实践中,您将同时使用两者
def mount(%{"house" => house}, %{"current_user_id" => user_id}, socket) do
temperature = Thermostat.get_house_reading(user_id, house)
{:ok, assign(socket, :temperature, temperature)}
end
换句话说,只要用户有权访问,您就需要读取有关特定房屋的信息。
绑定
Phoenix 支持 DOM 元素绑定以进行客户端-服务器交互。例如,要对按钮上的点击做出反应,您将渲染该元素
<button phx-click="inc_temperature">+</button>
然后在服务器上,所有 LiveView 绑定都由 handle_event/3
回调处理,例如
def handle_event("inc_temperature", _value, socket) do
{:noreply, update(socket, :temperature, &(&1 + 1))}
end
要更新 UI 状态(例如,打开和关闭下拉菜单、切换选项卡等),LiveView 还支持 JS 命令(Phoenix.LiveView.JS
),这些命令直接在客户端执行,而无需到达服务器。要了解有关此的更多信息,请参见 我们的绑定页面,了解所有 LiveView 绑定的完整列表,以及我们的 JavaScript 互操作性指南。
LiveView 内置支持表单,包括上传和关联管理。参见 Phoenix.Component.form/1
作为起点,以及 Phoenix.Component.inputs_for/1
了解如何处理关联。 上传 和 表单绑定 指南提供了有关高级功能的更多信息。
导航
LiveView 提供功能,允许使用 浏览器的 pushState API 进行页面导航。使用实时导航,页面将更新,而无需完全刷新页面。
您可以修补当前 LiveView,更新其 URL,或导航到新的 LiveView。您可以在 实时导航 指南中了解有关它们的更多信息。
生成器
Phoenix v1.6 及更高版本包含用于 LiveView 的代码生成器。如果您想查看如何构建应用程序的示例,从数据库一直到 LiveView,请运行以下命令
mix phx.gen.live Blog Post posts title:string body:text
有关更多信息,请运行 mix help phx.gen.live
。
对于身份验证,内置了 LiveView 支持,请运行 mix phx.gen.auth Account User users
。
将状态、标记和事件在 LiveView 中进行分隔
LiveView 支持两种扩展机制:函数组件(由 HEEx
模板提供)和有状态组件。
函数组件是接收 assigns 映射的任何函数,类似于我们 LiveView 中的 render(assigns)
,并返回一个 ~H
模板。例如
def weather_greeting(assigns) do
~H"""
<div title="My div" class={@class}>
<p>Hello <%= @name %></p>
<MyApp.Weather.city name="Kraków"/>
</div>
"""
end
您可以在 Phoenix.Component
模块中了解有关函数组件的更多信息。归根结底,它们是重用 LiveView 中标记的实用机制。
但是,有时您需要分隔或重用的内容不仅是标记。也许您想将 LiveView 中的一部分状态或部分事件移到另一个模块中。对于这些情况,LiveView 提供了 Phoenix.LiveComponent
,这些组件使用 live_component/1
渲染
<.live_component module={UserComponent} id={user.id} user={user} />
组件有自己的 mount/3
和 handle_event/3
回调,以及自己的具有更改跟踪支持的状态。组件也很轻量级,因为它们在与父 LiveView 相同的进程中“运行”。但是,这意味着组件中的错误会导致整个视图无法渲染。参见 Phoenix.LiveComponent
,了解有关组件的完整说明。
最后,如果您希望在 LiveView 的各个部分之间完全隔离,您始终可以通过调用 live_render/3
在另一个 LiveView 中渲染 LiveView。此子 LiveView 在与父级不同的进程中运行,并具有自己的回调。如果子 LiveView 崩溃,它不会影响父级。如果父级崩溃,则所有子级都会被终止。
渲染子 LiveView 时,:id
选项是必需的,以唯一标识子级。子 LiveView 只会渲染和安装一次,前提是其 ID 保持不变。要强制子级使用新的会话数据重新安装,必须提供新的 ID。
鉴于 LiveView 在自己的进程中运行,它是创建完全隔离的 UI 元素的绝佳工具,但如果您只想分隔标记或事件(或两者),它是一个略微昂贵的抽象。
总结一下
- 使用
Phoenix.Component
来分隔/重用标记 - 使用
Phoenix.LiveComponent
来分隔状态、标记和事件 - 使用嵌套的
Phoenix.LiveView
来分隔状态、标记、事件和错误隔离
指南
此文档分为两类。我们有所有 LiveView 模块的 API 参考,您可以在其中详细了解 Phoenix.Component
、Phoenix.LiveView
等。
LiveView 还提供了许多指南来帮助您完成旅程,这些指南分为服务器端和客户端
服务器端
这些指南侧重于服务器端功能
客户端
这些指南侧重于 LiveView 绑定和客户端集成