LiveView
Исходный кодТребование: предполагается, что вы ознакомились с вводными руководствами и запустили приложение Phoenix.
Требование: предполагается, что вы ознакомились с руководством по жизненному циклу запроса.
Мы уже рассмотрели, как работает типичный жизненный цикл запроса в Phoenix: запрос сопоставляется с маршрутом, контроллер обрабатывает запрос и обращается к представлению, чтобы вернуть ответ в нужном формате. Но что, если мы хотим создавать интерактивные страницы? В типичном приложении с рендерингом на сервере для изменения содержимого страницы требуется либо отправить форму и отобразить новую страницу, либо перенести логику приложения на клиент (например, с помощью JavaScript-фреймворков jQuery, React, Vue и т. д.) и создать API, с которым будет взаимодействовать клиент.
Phoenix LiveView предлагает другой подход: всё состояние хранится на сервере, а пользователям предоставляются насыщенные возможности взаимодействия в реальном времени на основе HTML, сформированного на сервере. Это альтернатива JavaScript-фреймворкам на стороне клиента, позволяющая создавать динамичные интерактивные приложения, используя на клиенте минимум кода JavaScript.
Что такое LiveView?
LiveView — это процессы, которые получают события, обновляют своё состояние и отображают изменения на странице в виде различий.
Модель программирования LiveView — декларативная: вместо того чтобы говорить «когда произойдёт событие X, измени Y на странице», события в LiveView представляют собой обычные сообщения, которые могут приводить к изменениям состояния. После изменения состояния LiveView повторно отрисовывает соответствующие части HTML-шаблона и отправляет их в браузер, который обновляет страницу наиболее эффективным способом.
Состояние LiveView — это не что иное, как функциональные неизменяемые структуры данных Elixir. События представляют собой либо внутренние сообщения приложения (обычно отправляемые через Phoenix.PubSub), либо сообщения, отправленные клиентом/браузером.
Каждый LiveView сначала статически отрисовывается в рамках обычного HTTP-запроса, что позволяет быстро достичь «первой значимой отрисовки» (First Meaningful Paint), а также помогает поисковым системам и системам индексирования. Затем между клиентом и сервером устанавливается постоянное соединение для обмена событиями и изменениями страницы. Благодаря этому приложения LiveView быстрее реагируют на события пользователя: по сравнению с запросами без сохранения состояния, которым при каждом обращении нужно выполнять аутентификацию, декодирование, загрузку и кодирование данных, здесь требуется меньше работы и передаётся меньше данных. LiveView можно представить как «различия, передаваемые по сети».
LiveView и Controller + View
Контроллеры Phoenix и LiveView выполняют схожие задачи по обработке взаимодействия с пользователем, но работают совершенно по-разному:
Controller + View
- Контроллеры обрабатывают каждую пару HTTP-запрос/ответ как отдельную транзакцию
- Для каждой загрузки страницы или отправки формы требуется полный цикл запроса и ответа
- Контроллеры не хранят состояние, а данные хранятся во внешних системах (базе данных, сеансе)
- Представления — это отдельные модули, которые отображают шаблоны с данными, полученными от контроллеров
- Для обновления страницы и динамического взаимодействия требуется либо полностью перезагружать страницу, либо писать собственный код JavaScript на стороне клиента
Подход LiveView
- При первоначальной загрузке страницы используется обычный жизненный цикл запроса, после чего устанавливается двунаправленное соединение с помощью Phoenix Channels
- Процесс LiveView сохраняет состояние на протяжении всего взаимодействия с пользователем
- Изменения состояния автоматически запускают повторную отрисовку только изменившихся частей страницы
- События передаются через постоянное соединение, а не отдельные HTTP-запросы
- Для интерактивных функций требуется минимум JavaScript
LiveView объединяет задачи контроллеров и представлений в более целостную модель.
Простой пример
LiveView входит в состав новых приложений Phoenix по умолчанию. Рассмотрим простой пример:
defmodule MyAppWeb.ThermostatLive do
use MyAppWeb, :live_view
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 показаны основные этапы жизненного цикла:
- Функция обратного вызова
mount/3инициализирует состояние при запуске LiveView - Функция
render/1определяет, что будет отображено, используя шаблоны HEEx - Функция обратного вызова
handle_event/3обрабатывает события от клиента
Чтобы подключить это в маршрутизаторе:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
pipeline :browser do
...
end
scope "/", MyAppWeb do
pipe_through :browser
...
live "/thermostat", ThermostatLive
end
end
После отрисовки LiveView отправляется обычный ответ 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 подключится по WebSocket, а mount/3 будет вызвана внутри запущенного процесса LiveView.
Основные понятия
Сокет и состояние
Сокет LiveView — это основная структура данных, в которой хранится всё состояние LiveView. Это неизменяемая структура, содержащая «назначенные значения» (assigns) — данные, доступные в шаблонах. Если у контроллеров есть conn, то у LiveView есть socket.
Изменения сокета (с помощью assign/3 или update/3) запускают повторную отрисовку. Всё состояние хранится на сервере, а клиенту отправляются только различия, что сокращает сетевой трафик.
Жизненный цикл LiveView
У LiveView есть несколько важных этапов жизненного цикла:
-
mount— инициализирует LiveView параметрами, данными сеанса и сокетом -
handle_params— обрабатывает изменения URL и соответствующим образом обновляет состояние LiveView -
handle_event— обрабатывает взаимодействия пользователя, поступающие от клиента -
handle_info— обрабатывает обычные сообщения процесса
Привязки DOM
LiveView предоставляет привязки DOM для удобного взаимодействия клиента с сервером:
<button phx-click="inc_temperature">+</button> <form phx-submit="save">...</form> <input phx-blur="validate">
Эти привязки автоматически отправляют события на сервер при возникновении указанных событий браузера. Затем они обрабатываются в handle_event/3.
Начало работы
Phoenix включает генераторы кода для LiveView. Попробуйте:
$ mix phx.gen.live Blog Post posts title:string body:text
Эта команда создаёт полноценную реализацию CRUD на LiveView, аналогичную mix phx.gen.html.
Подробнее о LiveView см. в документации Phoenix LiveView.
© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix.hexdocs.pm/live_view.html