视图基础 #
一、视图概述 #
1.1 什么是视图 #
在Phoenix中,视图模块负责处理模板渲染和提供辅助函数。Phoenix 1.7+使用新的视图结构,HTML模块替代了传统的View模块。
text
Controller → HTML模块 → 模板(.heex) → HTML输出
1.2 视图结构 #
text
lib/hello_web/
├── controllers/
│ ├── user_controller.ex
│ ├── user_html.ex # HTML视图模块
│ └── user_html/ # 模板目录
│ ├── index.html.heex
│ ├── show.html.heex
│ └── new.html.heex
└── components/
├── core_components.ex
└── layouts.ex
二、HTML模块 #
2.1 创建HTML模块 #
elixir
defmodule HelloWeb.UserHTML do
use HelloWeb, :html
embed_templates "user_html/*"
def format_date(date) do
Calendar.strftime(date, "%Y-%m-%d")
end
def user_name(user) do
"#{user.first_name} #{user.last_name}"
end
end
2.2 embed_templates #
elixir
defmodule HelloWeb.UserHTML do
use HelloWeb, :html
embed_templates "user_html/*"
end
这会将模板目录中的所有.heex文件编译为函数:
elixir
HelloWeb.UserHTML.index(assigns)
HelloWeb.UserHTML.show(assigns)
HelloWeb.UserHTML.new(assigns)
2.3 定义内联模板 #
elixir
defmodule HelloWeb.PageHTML do
use HelloWeb, :html
def home(assigns) do
~H"""
<div class="container">
<h1>Welcome to Phoenix!</h1>
<p>Start your journey here.</p>
</div>
"""
end
end
三、模板文件 #
3.1 模板命名 #
text
user_html/
├── index.html.heex # 列表页
├── show.html.heex # 详情页
├── new.html.heex # 新建表单
├── edit.html.heex # 编辑表单
└── _user.html.heex # 部分模板(下划线开头)
3.2 基本模板 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
3.3 使用Assigns #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
四、辅助函数 #
4.1 定义辅助函数 #
elixir
defmodule HelloWeb.UserHTML do
use HelloWeb, :html
embed_templates "user_html/*"
def format_date(nil), do: "N/A"
def format_date(date), do: Calendar.strftime(date, "%Y-%m-%d %H:%M")
def status_badge(:active), do: "bg-green-100 text-green-800"
def status_badge(:inactive), do: "bg-gray-100 text-gray-800"
def status_badge(:pending), do: "bg-yellow-100 text-yellow-800"
def truncate(text, length \\ 50) do
if String.length(text) > length do
String.slice(text, 0, length) <> "..."
else
text
end
end
end
4.2 在模板中使用 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
五、部分模板 #
5.1 创建部分模板 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
5.2 渲染部分模板 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
5.3 集合渲染 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
六、布局 #
6.1 应用布局 #
elixir
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
6.2 设置布局 #
elixir
defmodule HelloWeb.UserController do
use HelloWeb, :controller
plug :put_layout, "admin.html"
def index(conn, _params) do
render(conn, :index)
end
end
6.3 禁用布局 #
elixir
def show(conn, %{"id" => id}) do
user = Hello.Accounts.get_user!(id)
render(conn, :show, user: user, layout: false)
end
七、组件 #
7.1 函数组件 #
elixir
defmodule HelloWeb.CoreComponents do
use Phoenix.Component
attr :title, :string, required: true
attr :class, :string, default: ""
def card(assigns) do
~H"""
<div class={"card #{@class}"}>
<h2 class="card-title"><%= @title %></h2>
<%= render_slot(@inner_block) %>
</div>
"""
end
attr :type, :atom, values: [:info, :success, :warning, :error], default: :info
attr :message, :string, required: true
def alert(assigns) do
~H"""
<div class={"alert alert-#{@type}"}>
<%= @message %>
</div>
"""
end
end
7.2 使用组件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
7.3 带插槽的组件 #
elixir
attr :title, :string, required: true
slot :actions
slot :footer
def panel(assigns) do
~H"""
<div class="panel">
<div class="panel-header">
<h3><%= @title %></h3>
<div class="panel-actions">
<%= for action <- @actions do %>
<%= render_slot(action) %>
<% end %>
</div>
</div>
<div class="panel-body">
<%= render_slot(@inner_block) %>
</div>
<div class="panel-footer">
<%= render_slot(@footer) %>
</div>
</div>
"""
end
使用:
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
八、路由辅助函数 #
8.1 路径辅助函数 #
elixir
defmodule HelloWeb.UserHTML do
use HelloWeb, :html
embed_templates "user_html/*"
def user_link(conn, user) do
path = ~p"/users/#{user.id}"
link(user.name, to: path)
end
end
8.2 在模板中使用 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
九、表单辅助函数 #
9.1 表单组件 #
elixir
defmodule HelloWeb.CoreComponents do
use Phoenix.Component
attr :for, :any, required: true
attr :as, :atom, default: :form
def simple_form(assigns) do
~H"""
<.form :let={f} for={@for} as={@as}>
<%= render_slot(@inner_block, f) %>
</.form>
"""
end
attr :field, Phoenix.HTML.FormField, required: true
attr :label, :string, default: nil
attr :type, :atom, default: :text
def input(assigns) do
~H"""
<div class="form-group">
<label><%= @label || Phoenix.HTML.Form.humanize(@field.field) %></label>
<input type={@type} name={@field.name} value={@field.value} />
<%= if @field.errors do %>
<span class="error"><%= Enum.join(@field.errors, ", ") %></span>
<% end %>
</div>
"""
end
end
9.2 使用表单组件 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
十、国际化 #
10.1 Gettext配置 #
elixir
defmodule HelloWeb.Gettext do
use Gettext, otp_app: :hello
end
10.2 翻译文件 #
text
priv/gettext/
├── default.pot
├── en/
│ └── LC_MESSAGES/
│ └── default.po
└── zh/
└── LC_MESSAGES/
└── default.po
10.3 使用翻译 #
elixir
defmodule HelloWeb.UserHTML do
use HelloWeb, :html
import HelloWeb.Gettext
embed_templates "user_html/*"
def status_text(:active), do: gettext("Active")
def status_text(:inactive), do: gettext("Inactive")
end
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
十一、安全 #
11.1 HTML转义 #
Phoenix自动转义HTML:
elixir
user_input = "<script>alert('xss')</script>"
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
输出:<script>alert('xss')</script>
11.2 原始HTML #
elixir
def safe_html(html) do
{:safe, html}
end
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
11.3 CSRF保护 #
heex
defmodule HelloWeb.Layouts do
use HelloWeb, :html
embed_templates "layouts/*"
end
十二、总结 #
12.1 核心概念 #
| 概念 | 说明 |
|---|---|
| HTML模块 | 视图模块,定义辅助函数 |
| embed_templates | 嵌入模板文件 |
| assigns | 模板变量 |
| 组件 | 可复用的UI单元 |
| 布局 | 页面框架模板 |
12.2 常用函数 #
| 函数 | 说明 |
|---|---|
| render/2 | 渲染模板 |
| render/3 | 渲染带参数的模板 |
| link/2 | 生成链接 |
| form/2 | 生成表单 |
| ~H | HEEx模板宏 |
12.3 下一步 #
现在你已经了解了视图基础,接下来让我们学习 模板语法,深入了解HEEx模板!
最后更新:2026-03-28