LiveView简介 #
一、LiveView概述 #
1.1 什么是LiveView #
LiveView是Phoenix的实时视图技术,允许在服务端渲染富交互应用,无需编写JavaScript。
1.2 LiveView特点 #
- 服务端渲染
- 实时更新
- 无需JavaScript
- WebSocket通信
- 最小化DOM更新
1.3 LiveView架构 #
text
浏览器
│
▼
WebSocket连接
│
▼
LiveView进程
│
├── 状态管理
├── 事件处理
└── 模板渲染
│
▼
DOM补丁更新
二、创建LiveView #
2.1 定义LiveView #
elixir
defmodule HelloWeb.CounterLive do
use HelloWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, :count, 0)}
end
def render(assigns) do
~H"""
<div>
<h1>Count: <%= @count %></h1>
<button phx-click="increment">+</button>
<button phx-click="decrement">-</button>
</div>
"""
end
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
def handle_event("decrement", _params, socket) do
{:noreply, update(socket, :count, &(&1 - 1))}
end
end
2.2 路由配置 #
elixir
defmodule HelloWeb.Router do
use Phoenix.Router
live "/counter", CounterLive
live "/users", UserLive.Index
live "/users/:id", UserLive.Show
end
2.3 生成LiveView资源 #
bash
mix phx.gen.live Accounts User users name:string email:string
三、生命周期 #
3.1 mount/3 #
elixir
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Hello.PubSub, "updates")
end
{:ok, assign(socket, :users, Hello.Accounts.list_users())}
end
3.2 handle_params/3 #
elixir
def handle_params(params, _url, socket) do
page = Map.get(params, "page", "1") |> String.to_integer()
users = Hello.Accounts.list_users(page: page)
{:noreply, assign(socket, :users, users)}
end
3.3 render/1 #
elixir
def render(assigns) do
~H"""
<div>
<ul>
<%= for user <- @users do %>
<li><%= user.name %> - <%= user.email %></li>
<% end %>
</ul>
</div>
"""
end
3.4 terminate/2 #
elixir
def terminate(_reason, socket) do
cleanup_resources(socket)
:ok
end
四、状态管理 #
4.1 assign/3 #
elixir
def mount(_params, _session, socket) do
socket =
socket
|> assign(:users, [])
|> assign(:page, 1)
|> assign(:total_pages, 0)
{:ok, socket}
end
4.2 assign_new/3 #
elixir
def mount(_params, session, socket) do
socket =
socket
|> assign_new(:current_user, fn ->
get_user_from_session(session)
end)
{:ok, socket}
end
4.3 update/3 #
elixir
def handle_event("increment", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
def handle_event("add_user", %{"name" => name}, socket) do
{:noreply, update(socket, :users, fn users -> [name | users] end)}
end
五、事件处理 #
5.1 点击事件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
elixir
def handle_event("delete", %{"id" => id}, socket) do
{:ok, _} = Hello.Accounts.delete_user(id)
{:noreply, assign(socket, :users, Hello.Accounts.list_users())}
end
5.2 表单事件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
elixir
def handle_event("validate", %{"user" => user_params}, socket) do
changeset = Hello.Accounts.change_user(%User{}, user_params)
{:noreply, assign(socket, :changeset, changeset)}
end
def handle_event("save", %{"user" => user_params}, socket) do
case Hello.Accounts.create_user(user_params) do
{:ok, user} ->
{:noreply,
socket
|> put_flash(:info, "User created")
|> push_navigate(to: ~p"/users/#{user.id}")}
{:error, changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
5.3 键盘事件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
elixir
def handle_event("search", %{"value" => query}, socket) do
users = Hello.Accounts.search_users(query)
{:noreply, assign(socket, :users, users)}
end
5.4 焦点事件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
六、导航 #
6.1 push_navigate #
elixir
def handle_event("redirect", _params, socket) do
{:noreply, push_navigate(socket, to: ~p"/users")}
end
6.2 push_patch #
elixir
def handle_event("change_page", %{"page" => page}, socket) do
{:noreply, push_patch(socket, to: ~p"/users?page=#{page}")}
end
6.3 redirect #
elixir
def handle_event("external_redirect", _params, socket) do
{:noreply, redirect(socket, external: "https://example.com")}
end
七、Flash消息 #
7.1 设置Flash #
elixir
def handle_event("save", %{"user" => user_params}, socket) do
case Hello.Accounts.create_user(user_params) do
{:ok, user} ->
{:noreply,
socket
|> put_flash(:info, "User created successfully")
|> push_navigate(to: ~p"/users/#{user.id}")}
{:error, changeset} ->
{:noreply,
socket
|> put_flash(:error, "Failed to create user")
|> assign(:changeset, changeset)}
end
end
7.2 显示Flash #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
八、定时器 #
8.1 定时更新 #
elixir
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(1000, :tick)
end
{:ok, assign(socket, :time, DateTime.utc_now())}
end
def handle_info(:tick, socket) do
{:noreply, assign(socket, :time, DateTime.utc_now())}
end
8.2 倒计时 #
elixir
def mount(_params, _session, socket) do
if connected?(socket) do
Process.send_after(self(), :countdown, 1000)
end
{:ok, assign(socket, :seconds, 60)}
end
def handle_info(:countdown, socket) do
if socket.assigns.seconds > 0 do
Process.send_after(self(), :countdown, 1000)
{:noreply, update(socket, :seconds, &(&1 - 1))}
else
{:noreply, socket}
end
end
九、PubSub集成 #
9.1 订阅消息 #
elixir
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(Hello.PubSub, "users")
end
{:ok, assign(socket, :users, Hello.Accounts.list_users())}
end
def handle_info({:user_created, user}, socket) do
{:noreply, update(socket, :users, fn users -> [user | users] end)}
end
def handle_info({:user_deleted, user_id}, socket) do
{:noreply, update(socket, :users, fn users ->
Enum.reject(users, fn u -> u.id == user_id end)
end)}
end
9.2 广播消息 #
elixir
def handle_event("create_user", %{"user" => user_params}, socket) do
case Hello.Accounts.create_user(user_params) do
{:ok, user} ->
Phoenix.PubSub.broadcast(Hello.PubSub, "users", {:user_created, user})
{:noreply, push_navigate(socket, to: ~p"/users/#{user.id}")}
{:error, changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
十、总结 #
10.1 核心概念 #
| 概念 | 说明 |
|---|---|
| mount | 初始化状态 |
| render | 渲染模板 |
| handle_event | 处理事件 |
| handle_info | 处理消息 |
| assign | 状态管理 |
10.2 常用绑定 #
| 绑定 | 说明 |
|---|---|
| phx-click | 点击事件 |
| phx-change | 表单变化 |
| phx-submit | 表单提交 |
| phx-key | 键盘事件 |
| phx-hook | JavaScript钩子 |
10.3 下一步 #
现在你已经了解了LiveView基础,接下来让我们学习 LiveView组件,深入了解组件化开发!
最后更新:2026-03-28