LiveView #
一、LiveView概述 #
1.1 什么是LiveView #
LiveView是Phoenix的实时渲染引擎,允许使用服务端渲染构建富交互应用。
1.2 LiveView优势 #
- 无需JavaScript框架
- 实时更新
- 服务端渲染
- WebSocket通信
二、创建LiveView #
2.1 路由配置 #
elixir
defmodule MyAppWeb.Router do
use MyAppWeb, :router
scope "/", MyAppWeb do
pipe_through :browser
live "/", PageLive, :index
live "/users", UserLive.Index, :index
live "/users/new", UserLive.Index, :new
live "/users/:id", UserLive.Show, :show
end
end
2.2 LiveView模块 #
elixir
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
alias MyApp.Accounts
alias MyApp.Accounts.User
@impl true
def mount(_params, _session, socket) do
{:ok, stream(socket, :users, Accounts.list_users())}
end
@impl true
def render(assigns) do
~H"""
<h1>Users</h1>
<ul id="users" phx-update="stream">
<li :for={{dom_id, user} <- @streams.users} id={dom_id}>
<%= user.name %>
</li>
</ul>
"""
end
end
三、状态管理 #
3.1 mount #
elixir
@impl true
def mount(_params, _session, socket) do
socket =
socket
|> assign(:users, Accounts.list_users())
|> assign(:page_title, "Users")
{:ok, socket}
end
3.2 assign #
elixir
socket = assign(socket, :name, "Alice")
socket = assign(socket, name: "Alice", age: 30)
3.3 update #
elixir
socket = update(socket, :count, &(&1 + 1))
四、事件处理 #
4.1 处理事件 #
elixir
@impl true
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
@impl true
def handle_event("add_user", %{"user" => user_params}, socket) do
case Accounts.create_user(user_params) do
{:ok, user} ->
{:noreply, stream_insert(socket, :users, user)}
{:error, changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
4.2 模板事件 #
html
<button phx-click="increment">
Increment
</button>
<form phx-submit="save">
<input type="text" name="name" />
<button type="submit">Save</button>
</form>
<div phx-window-keydown="keydown">
Press any key
</div>
4.3 JS命令 #
html
<button phx-click={JS.push("delete", value: %{id: user.id})}>
Delete
</button>
<button phx-click={JS.toggle(to: "#details")}>
Toggle
</button>
<button phx-click={JS.hide(to: "#modal")}>
Close
</button>
五、组件 #
5.1 函数组件 #
elixir
defmodule MyAppWeb.Components do
use Phoenix.Component
attr :user, :map, required: true
def user_card(assigns) do
~H"""
<div class="user-card">
<h2><%= @user.name %></h2>
<p><%= @user.email %></p>
</div>
"""
end
end
5.2 Live组件 #
elixir
defmodule MyAppWeb.UserLive.FormComponent do
use MyAppWeb, :live_component
alias MyApp.Accounts
@impl true
def render(assigns) do
~H"""
<div>
<.simple_form for={@form} phx-change="validate" phx-submit="save">
<.input field={@form[:name]} type="text" label="Name" />
<.input field={@form[:email]} type="email" label="Email" />
<:actions>
<.button>Save</.button>
</:actions>
</.simple_form>
</div>
"""
end
@impl true
def handle_event("validate", %{"user" => user_params}, socket) do
changeset = Accounts.change_user(socket.assigns.user, user_params)
{:noreply, assign(socket, :form, to_form(changeset, action: :validate))}
end
@impl true
def handle_event("save", %{"user" => user_params}, socket) do
Accounts.update_user(socket.assigns.user, user_params)
{:noreply, socket}
end
end
5.3 使用组件 #
html
<.live_component module={MyAppWeb.UserLive.FormComponent} id={@user.id} user={@user} />
六、实时更新 #
6.1 PubSub #
elixir
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
MyApp.Accounts.subscribe()
end
{:ok, stream(socket, :users, Accounts.list_users())}
end
@impl true
def handle_info({:user_created, user}, socket) do
{:noreply, stream_insert(socket, :users, user)}
end
@impl true
def handle_info({:user_deleted, user}, socket) do
{:noreply, stream_delete(socket, :users, user)}
end
end
6.2 发布事件 #
elixir
defmodule MyApp.Accounts do
def create_user(attrs) do
{:ok, user} = Repo.insert(changeset)
MyApp.Endpoint.broadcast!("users", "user_created", user)
{:ok, user}
end
end
七、总结 #
本章学习了:
| 特性 | 用途 |
|---|---|
| mount | 初始化状态 |
| assign | 管理状态 |
| handle_event | 处理事件 |
| 组件 | 可复用UI |
| PubSub | 实时更新 |
准备好学习测试了吗?让我们进入下一章。
最后更新:2026-03-27