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