やってみよう!Phoenix Live View その②

目次

エントリポイント

LiveViewプロセス

プロセス

すべての Elixir コードは仮想マシン BEAM の上で動きます。BEAM の上で動いている Elixir コードをプロセス(process)と呼びます。

各プロセスは固有の状態メールボックスを持っています。プロセス同士はメッセージを送り合うことができます。メッセージはプロセスのメールボックスに入ります。プロセスはメールボックスに届いたメッセージを読み取り、その内容に応じて自身の状態を更新します。

LiveViewプロセスの生と死

ブラウザと Phoenix サーバーの間で WebSocket 通信が成立すると Phoenix サーバー側で LiveView プロセス が誕生し、 WebSocket 通信が途絶えると死にます。生まれてから死ぬまでの間、LiveView プロセスのメールボックスにはブラウザからのメッセージが次々と届きます。

LiveView プロセスとブラウザは一対一の関係にあります。1 万個のブラウザが Phoenix LiveView ベースのシングルページアプリケーションを開いているとすれば、仮想マシン BEAM の中に 1 万個の LiveView プロセスが存在し、メッセージを待ち受けていることになります。

LiveViewソケット

LiveView プロセスの状態を LiveView ソケットと呼びます。日常用語の「ソケット」は電球を取り付ける電気器具を意味しますが、そのイメージに引きずられないでください。

LiveView ソケットの実体は、Phoenix.LiveView.Socket 構造体です。生まれてから死ぬまでの間、LiveView プロセスはこの構造体を保持し続けます。

LiveViewモジュールとLiveViewプロセスの関係

前章で RobotLive という名前の LiveView モジュールを定義しました。これと LiveView プロセスはどのような関係にあるのでしょうか。

LiveView モジュールは「箱」のようなものです。その中には複数の関数が入っています。LiveView プロセスは誕生時に特定の「箱」と結び付けられます。

LiveView プロセスのメールボックスにメッセージが届くと、その内容に応じて「箱」の中にある特定の関数が選び出され、その関数にメッセージと自分自身の状態(LiveView ソケット)を引数として渡します。その関数は、受け取った LiveView ソケットを更新して返します。これが LiveView プロセスの新たな状態となります。

エントリポイント

関数 mount/3

LiveView モジュールの関数 mount/3エントリポイント(entry-point)と呼ばれます。シングルページアプリケーションが初期化される際にこの関数が呼び出されます。

エントリポイント mount/3 は 3 つの引数を取ります。

  1. パラメータ
  2. セッション
  3. LiveView ソケット

パラメータに関しては第 5 章で説明しました。セッションに関しては(将来刊行される)別の巻で説明する予定です。

では、RobotLive モジュールにエントリポイントを加えましょう。robot_live.ex を次のように書き換えてください。

defmodule PhxSampleWeb.RobotLive do
  use PhxSampleWeb, :live_view

  def mount(_params, _session, socket) do
    socket =
      socket
      |> assign(:counter, 0)
      |> assign(:name, "world")

    {:ok, socket}
  end
end

第 3 引数 socket には空の LiveView ソケット(LiveView プロセスの状態を保持する構造体)が渡されます。エントリポイントの主な役割は、LiveView ソケットの初期化です。

assignマップと関数 assign/3

LiveView ソケットには assigns というマップ型のフィールドがあります。Elixir の公式ドキュメントではこれを「socket.assigns」あるいは「assigns」と呼んでいますが、本書ではassigns マップと呼びます。

assigns マップにセットされた値は、LiveView プロセスが HTML 文書をレンダリングする際に参照されます。

関数 assign/3 は、assigns マップに値をセットするために利用されます。

例えば次のように書くと、"Red" という値を持つ :color というキーが assigns マップに追加されます。

socket = assign(socket, :color, "Red")

さて、robot_live.ex の 5-8 行をご覧ください。

socket =
  socket
  |> assign(:counter, 0)
  |> assign(:name, "world")

Elixir の特色であるパイプ演算子|>)が使われています。パイプ演算子を使わずに書き直すと、次のようになります。

socket = assign(socket, :counter, 0)
socket = assign(socket, :name, "world")

つまり、robot_live.ex の 5-8 行では、LiveView ソケットの assigns マップに対して 0 および "world" という 2 つの値をセットしているというわけです。

assignマップの値をブラウザ上に表示する

エントリポイントの中で LiveView ソケットの assigns マップにセットされた値は @ マクロにより、HEEx テンプレートの中に埋め込むことができます。

robot_live.html.heex を次のように書き換えてください。

<div><span class="font-bold">counter:</span>{@counter}</div>
<div><span class="font-bold">name:</span>{@name}</div>