檢視來源 歡迎
歡迎來到 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/3
和 handle_event/3
回呼函式,以及支援變更追蹤的自己的狀態。元件也很輕量級,因為它們「執行」於與父元件相同的處理程序。但這樣一來,如果元件發生錯誤,就會導致整個檢視無法渲染。請參閱 Phoenix.LiveComponent
來完整瞭解元件。
最後,如果你想在 LiveView 的各部分之間達到完全隔離,你可以隨時在另一個 LiveView 中呼叫 live_render/3
來渲染 LiveView。這個子 LiveView 會在與父元件不同的處理程序執行,並有自己的回呼函式。如果子 LiveView 發生崩潰,它不會影響父元件。如果父元件發生崩潰,所有子元件都會被終止。
在渲染子 LiveView 時,:id
選項是必須的,用於給子元件建立唯一的識別。子 LiveView 只會渲染和掛載一次,只要它的 ID 不變。若要強制子元件用新的工作階段資料重新掛載,必須提供新的 ID。
由於 LiveView 執行於自身的處理程序,所以它是建立完全隔離的 UI 元件的絕佳工具,但如果你只想隔離標記或事件(或兩者),它會是一個稍微昂貴的抽象。
總結來說
- 使用
Phoenix.Component
來隔離/重複使用標記 - 使用
Phoenix.LiveComponent
來隔離狀態、標記和事件 - 使用內嵌的
Phoenix.LiveView
來隔離狀態、標記、事件和錯誤隔離
指南
此文件分為兩大部分。我們有 LiveView 模組的 API 說明,您可以在那裡進一步了解 Phoenix.Component
、Phoenix.LiveView
等。
LiveView 還有許多教學可以幫助您學習,分為伺服器端和客戶端教學
伺服器端
這些教學重點在伺服器端功能
客戶端
這些教學重點在 LiveView 繫結與客戶端整合