先别急着进 LiveView 和 Channels。
Phoenix 先是一套非常扎实的 HTTP 城邦。
一次普通请求的流转,大概像这样:
浏览器请求
│
▼
Endpoint 城门
│
▼
Plug 安检流水线
│
▼
Router 分流到对应道路
│
▼
Controller 办理业务
│
▼
HTML/JSON 渲染
│
▼
Response 回执出城Phoenix 官方路由文档长期强调两件事:
- 路由会被编译成高效的匹配结构
- 每条路由还会生成
Phoenix.VerifiedRoutes元数据
这意味着在 Phoenix 里,路由不是“运行时字符串碰碰运气”,而是编译期就尽量帮你发现坏链接和坏路径。
看一个最小例子:
defmodule CityWeb.Router do
use CityWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, html: {CityWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :fetch_current_scope_for_user
end
scope "/", CityWeb do
pipe_through :browser
get "/", PageController, :home
get "/dispatches", DispatchController, :index
end
end上面这段你要这样理解:
pipeline :browser:入城默认安检线plug:每一道安检闸机scope "/":这一片城区get "/dispatches":这条街的办事窗口是谁
Controller 看起来也很朴素:
defmodule CityWeb.DispatchController do
use CityWeb, :controller
alias City.Dispatch
def index(conn, _params) do
dispatches = Dispatch.list_dispatches(conn.assigns.current_scope)
render(conn, :index, dispatches: dispatches)
end
end对应 HTML 层:
defmodule CityWeb.DispatchHTML do
use CityWeb, :html
def index(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<section>
<h1>今日调度公报</h1>
<ul>
<li :for={dispatch <- @dispatches}>
{dispatch.title} - {dispatch.status}
</li>
</ul>
<.link href={~p"/live/dispatches"}>进入实时调度台</.link>
</section>
</Layouts.app>
"""
end
end这里有三个 Phoenix 味道很重的点:
render(conn, :index, ...)不是去找“古早 view 魔法”,而是去调用对应 HTML 模块里的函数组件模板。~H是 HEEx,不只是字符串模板,而是 HTML 感知模板。~p"/live/dispatches"是编译期校验过的路径,不是裸字符串。
所以 Phoenix 的 HTTP 层很像一座秩序极好的政务大厅:
路怎么走,窗口谁办,公文怎么渲染,都是清楚的。