檢視來源 歡迎

歡迎來到 Phoenix LiveView 文件。Phoenix LiveView 透過伺服器渲染的 HTML 啟用豐富的即時使用者體驗。有關 LiveView 和其優點的概觀,在我們的 README 中提供

什麼是 LiveView?

LiveView 是接收事件、更新其狀態並將更新渲染到頁面作為 diff 的處理序。

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 是我們儲存所有狀態的地方,包括 assign。 mount 將預設溫度指定給 Socket。由於 Elixir 資料結構是不可變的,因此 LiveView API 通常接收 Socket 並傳回更新後的 Socket。然後我們傳回 {:ok, socket} 表示我們已成功 mount LiveView。在 mount 之後,LiveView 會使用 assigns 的值呈現在頁面上,並把它傳送至用戶端。

如果您檢視已呈現在頁面上的 HTML,您會注意到有一個按鈕,上面有一個 phx-click 的屬性。當按鈕被點選時,會傳送一個 "inc_temperature" 的事件至伺服器,而這個事件會被 handle_event 的回呼函式所配對並處理。此回呼函式會更新 Socket 並傳回 {:noreply, socket},其中包含已更新的 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 已呈現在頁面上,就會傳送一個 regular 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 用戶端會透過 WebSockets 進行連線,並且 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 範本提供),和有狀態元件。

函式元件是可以接收指定 map 的函數,類似 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/3handle_event/3 回呼函式,以及支援變更追蹤的自己的狀態。元件也很輕量級,因為它們「執行」於與父元件相同的處理程序。但這樣一來,如果元件發生錯誤,就會導致整個檢視無法渲染。請參閱 Phoenix.LiveComponent 來完整瞭解元件。

最後,如果你想在 LiveView 的各部分之間達到完全隔離,你可以隨時在另一個 LiveView 中呼叫 live_render/3 來渲染 LiveView。這個子 LiveView 會在與父元件不同的處理程序執行,並有自己的回呼函式。如果子 LiveView 發生崩潰,它不會影響父元件。如果父元件發生崩潰,所有子元件都會被終止。

在渲染子 LiveView 時,:id 選項是必須的,用於給子元件建立唯一的識別。子 LiveView 只會渲染和掛載一次,只要它的 ID 不變。若要強制子元件用新的工作階段資料重新掛載,必須提供新的 ID。

由於 LiveView 執行於自身的處理程序,所以它是建立完全隔離的 UI 元件的絕佳工具,但如果你只想隔離標記或事件(或兩者),它會是一個稍微昂貴的抽象。

總結來說

指南

此文件分為兩大部分。我們有 LiveView 模組的 API 說明,您可以在那裡進一步了解 Phoenix.ComponentPhoenix.LiveView 等。

LiveView 還有許多教學可以幫助您學習,分為伺服器端和客戶端教學

伺服器端

這些教學重點在伺服器端功能

客戶端

這些教學重點在 LiveView 繫結與客戶端整合