正在加载,请稍候…

Elixir Phoenix LiveView: Real-Time Apps Without JavaScript

Build real-time web apps with Phoenix LiveView — stateful server components, PubSub, live components, JS hooks, and deploying to Fly.io.

Phoenix LiveView Philosophy

LiveView keeps state on the server and sends only HTML diffs over WebSocket. No JSON API, no client state management needed.

Basic Counter LiveView

defmodule MyAppWeb.CounterLive do
  use MyAppWeb, :live_view

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

  def render(assigns) do
    ~H"""
    <div>
      <h1>Count: <%= @count %></h1>
      <button phx-click="inc">+</button>
      <button phx-click="dec">-</button>
    </div>
    """
  end

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

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

Real-Time Chat with PubSub

defmodule MyAppWeb.ChatLive do
  use MyAppWeb, :live_view

  def mount(%{"room_id" => room_id}, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "room:" <> room_id)
    end
    {:ok, assign(socket, messages: [], room_id: room_id)}
  end

  def handle_event("send", %{"text" => text}, socket) do
    msg = %{text: text, user: socket.assigns.current_user}
    Phoenix.PubSub.broadcast(MyApp.PubSub, "room:" <> socket.assigns.room_id, {:msg, msg})
    {:noreply, socket}
  end

  def handle_info({:msg, msg}, socket) do
    {:noreply, update(socket, :messages, &(&1 ++ [msg]))}
  end
end

Live Components

defmodule MyAppWeb.CardComponent do
  use MyAppWeb, :live_component

  def render(assigns) do
    ~H"""
    <div id={"card-" <> @item.id}>
      <h3><%= @item.title %></h3>
      <button phx-click="like" phx-target={@myself}>Like (<%= @likes %>)</button>
    </div>
    """
  end

  def handle_event("like", _params, socket) do
    {:noreply, update(socket, :likes, &(&1 + 1))}
  end
end

JS Hooks for Client-Side Interop

// app.js
let Hooks = {}
Hooks.Chart = {
  mounted() {
    this.chart = new Chart(this.el, { type: 'line', data: JSON.parse(this.el.dataset.values) })
    this.handleEvent("update_chart", ({values}) => {
      this.chart.data.datasets[0].data = values
      this.chart.update()
    })
  }
}
<canvas id="my-chart" phx-hook="Chart" data-values={Jason.encode!(@chart_data)}></canvas>

Deploy to Fly.io

fly launch
fly deploy
fly scale count 2

LiveView apps on Fly.io distribute WebSocket connections via clustering with libcluster.