Phoenix LiveViewによる動的サーバーサイドレンダリング3

はじめに

「Phoenix LiveViewによる動的サーバーサイドレンダリング」の3回目です。
2回目と同様にカウンターを作成します。
今回は LiveView を使います。

  1. Phoenix LiveViewのセットアップ方法
  2. Phoenix LiveViewを使用しないカウンターの実装
  3. Phoenix LiveViewを使用したカウンターの実装(この記事)

LiveViewのセットアップに関しては、こちらに記述があります。

Update: バージョン0.10を用いた最新のインストール方法は、Phoenix.LiveViewの最新インストール方法で紹介しています。

LiveViewページの作成

LiveView ページの作成を行います。 作成するページは、Phoenix LiveViewを使用しないカウンターの実装と同じシンプルなカウンタです。

動作としては、ご推察のとおり「ー」ボタンを押下するとページの再読み込みなしにカウント値が減少し、「+」ボタンを押下するとカウント値が増加します。

LvDemoCounter

以下の図の黄色い部分を LiveView を使って作成していきます。

LiveVIew1

Router

まずは Router から実装します。 /live-counter にアクセスされたら目的のページを表示するようにします。
ページの実装は、CounterLive で行います。 今までと違うのは、get,resource等ではなく Phoenix.LiveView.Router.live を使用するところです。

scope "/", LvDemoWeb do
  pipe_through :browser
  ...
  live "/live-counter", CounterLive  ...
end

LiveView

ここがメインの部分になります。
lib/lv_demo_web/live/ の下に counter_live.ex というファイルを作成し、そこに処理を記述します。

状態が変わったときに呼び出される関数を記述します。 以下のような構成になっています。

関数役割
render描画処理を行う
mount初回表示時に呼ばれページの初期値を決める
handle_eventクライアント側から送信されたイベントに対応する

ソースコードは以下の様になります。

# lib/lv_demo_web/live/counter_live.ex
defmodule LvDemoWeb.CounterLive do
  use Phoenix.LiveView

  def render(assigns) do
    LvDemoWeb.CounterLiveView.render("index.html", assigns)
  end

  def mount(_session, socket) do
    {:ok, assign(socket, :val, 0)}
  end

  def handle_event("inc", _, socket) do
    {:noreply, update(socket, :val, &(&1 + 1))}
  end

  def handle_event("dec", _, socket) do
    {:noreply, update(socket, :val, &(&1 - 1))}
  end

end

Viewの実装

Viewを実装します。
Viewに関しては、特に変わった記述はありません。

# lib/lv_demo_web/views/counter_live_view.ex
defmodule LvDemoWeb.CounterLiveView do
  use LvDemoWeb, :view
end

テンプレートの実装

まずテンプレートの置き場所は、通常のルール通りに行います。 LiveView で動的にページを変更するためのテンプレートは、拡張子を leex にします。
この拡張子にすることで Phoenix 側ではLiveViewのテンプレートであるとして扱われます。
以上を踏まえて以下のファイルを作成します。

lib/lv_demo_web/templates/counter_live/index.html.leex

実装は以下になります。

# lib/lv_demo_web/templates/counter_live/index.html.leex
<div>
  <h1>The count is: <%= @val %></h1>
  <button phx-click="dec">-</button>
  <button phx-click="inc">+</button>
</div>

ファイルの拡張子以外で通常のテンプレートと違うのは以下の部分になります。

phx-click="some-event"

これはLiveViewで導入された記述であり、要素がクリックされたら some-event というイベント名でサーバー側にWebSocketで通信します。

これで全ての実装が完了しました。

処理の流れについて

以下に処理の流れを記述します。
概要は以下です。

  1. 静的なサイトの構築
  2. 動的なサイトにアップデート
  3. WebSocketを使用してクライアントとサーバーをステートフルに

シーケンス図にすると以下の様になります。

ClientServerGET "/live-counter"CounterLive.mountCounterLive.render200 HTMLWS ConnectLiveView.Channel.start_linkPID<1,2,3>CounterLive.mountCounterLive.renderHTMLExample of phx-clickphx-click="inc"CounterLive.handle_event("inc",...)status updateCounterLive.renderDiffApply Patchloop[ PID<1.2.3> ]ClientServer

参考動画

詳しい説明は Phoenix の作者が ElixirConf EU 2019 で説明しています。
参考動画は以下になります。

Chris McCord - Keynote: Phoenix LiveView - Interactive Apps without Javascript - ElixirConf EU 2019

以下ではライフサイクルの説明から始まります。

LiveView Lifecycle

差分を作成についての詳しい説明は以下にあります。

LiveEEX Optimizations

おわりに

Phoenix LiveView について3回に分けて記述しました。
LiveViewを使ってみて以下のメリットを感じました。

  • コードの記述量少ない
  • クライアント側もElixirで実装できシンプル

セットアップに関する記述を含まない場合、LiveViewを使わなかったときに比べてコードの記述量が少なくなります。
セットアップについては将来的に標準搭載になったらほとんど必要なくなると予想されます。
クライアント側で必要なJavaScriptコードは、セットアップ時にインポートするphoenixliveviewライブラリが受け持ってくれます。
そのためこの部分のコードがいらなくなり、結果的にElixirのみで開発できることになります。
結果的に実装がシンプルになり、コードのメンテもしやすくなると予想されます。

今までPhoenixでWebアプリを開発している開発者で、クライアント側をSPAにしたい&JavaScriptは使いたくないために Elm 等の言語を使用していた(自分みたいな)人には一つの強力な選択肢になります。

Phoenix Framework の作者Chris McCordさんのtwitterでも固定ツイートされているとおり、現在 Phoenix のイチオシ機能となっています。

LiveViewに関する応用例は、いろいろな方が公開しています。 そのような例がPhoenix Framework LiveView Collectionにもいろいろあります。

予定している機能にもいろいろ魅力的なものがあります。
参考動画は以下になります。以下ではライフサイクルの説明から始まります。 Next Steps

とはいえ、LiveViewにも仕組み上できないことはありますし、始まったばかりの機能でもあります。 自分でもいろいろな適用例を生み出しつつ、積極的に見ていきたいと思います。