Rails ERB模板 #
一、ERB概述 #
1.1 什么是ERB #
ERB(Embedded Ruby)是Rails默认的模板引擎,允许在HTML中嵌入Ruby代码。
1.2 ERB标签 #
| 标签 | 说明 | 示例 |
|---|---|---|
<% %> |
执行Ruby代码,不输出 | <% @article.save %> |
<%= %> |
执行并输出结果 | <%= @article.title %> |
<% -%> |
执行代码,去除后面换行 | <% if true -%> |
<%# %> |
注释 | <%# 这是一个注释 %> |
二、输出内容 #
2.1 基本输出 #
erb
<!-- 输出变量 -->
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<!-- 输出表达式 -->
<p>文章数量:<%= Article.count %></p>
<p>当前时间:<%= Time.now.strftime('%Y-%m-%d') %></p>
<!-- 输出链接 -->
<%= link_to '首页', root_path %>
<%= link_to '文章详情', article_path(@article) %>
2.2 HTML转义 #
erb
<!-- 自动转义HTML(默认) -->
<%= '<script>alert("XSS")</script>' %>
<!-- 输出: <script>alert("XSS")</script> -->
<!-- 原样输出HTML -->
<%= raw '<strong>粗体文字</strong>' %>
<!-- 或使用html_safe -->
<%= '<strong>粗体文字</strong>'.html_safe %>
<!-- 安全连接 -->
<%= safe_join(['项目1', '项目2'], ', ') %>
2.3 sanitize #
erb
<!-- 过滤危险标签,保留安全标签 -->
<%= sanitize @article.body %>
<!-- 允许特定标签和属性 -->
<%= sanitize @article.body, tags: %w[p strong em a], attributes: %w[href] %>
三、控制结构 #
3.1 条件判断 #
erb
<!-- if语句 -->
<% if @article.published? %>
<span class="badge">已发布</span>
<% elsif @article.draft? %>
<span class="badge">草稿</span>
<% else %>
<span class="badge">未知状态</span>
<% end %>
<!-- unless语句 -->
<% unless @article.comments.empty? %>
<p>评论数:<%= @article.comments.count %></p>
<% end %>
<!-- 三元运算符 -->
<p>状态:<%= @article.published? ? '已发布' : '未发布' %></p>
3.2 循环 #
erb
<!-- each循环 -->
<% @articles.each do |article| %>
<div class="article">
<h2><%= article.title %></h2>
<p><%= truncate(article.body, length: 100) %></p>
</div>
<% end %>
<!-- each_with_index -->
<% @articles.each_with_index do |article, index| %>
<div class="article article-<%= index %>">
<h2><%= index + 1 %>. <%= article.title %></h2>
</div>
<% end %>
<!-- for循环(不推荐) -->
<% for article in @articles %>
<div class="article">
<h2><%= article.title %></h2>
</div>
<% end %>
<!-- times循环 -->
<% 5.times do |i| %>
<p>第 <%= i + 1 %> 次</p>
<% end %>
3.3 case语句 #
erb
<% case @article.status %>
<% when 'published' %>
<span class="badge bg-success">已发布</span>
<% when 'draft' %>
<span class="badge bg-secondary">草稿</span>
<% when 'archived' %>
<span class="badge bg-dark">已归档</span>
<% else %>
<span class="badge bg-warning">未知</span>
<% end %>
四、部分视图 #
4.1 渲染部分视图 #
erb
<!-- 渲染部分视图 -->
<%= render 'shared/header' %>
<!-- 渲染 app/views/shared/_header.html.erb -->
<!-- 渲染集合 -->
<%= render @articles %>
<!-- 渲染 app/views/articles/_article.html.erb,自动传递article变量 -->
<!-- 渲染集合带局部变量 -->
<%= render partial: 'article', collection: @articles, as: :item %>
<!-- 渲染单个对象 -->
<%= render @article %>
<!-- 渲染 app/views/articles/_article.html.erb -->
4.2 部分视图文件 #
erb
<!-- app/views/articles/_article.html.erb -->
<div class="article">
<h2><%= article.title %></h2>
<p><%= truncate(article.body, length: 100) %></p>
<div class="meta">
<span>作者:<%= article.author.name %></span>
<span>时间:<%= article.created_at.strftime('%Y-%m-%d') %></span>
</div>
</div>
4.3 传递局部变量 #
erb
<!-- 传递局部变量 -->
<%= render 'shared/card', title: '文章统计', count: @articles.count %>
<!-- app/views/shared/_card.html.erb -->
<div class="card">
<div class="card-header">
<%= title %>
</div>
<div class="card-body">
<p>数量:<%= count %></p>
</div>
</div>
4.4 集合渲染优化 #
erb
<!-- 使用cached选项缓存 -->
<%= render partial: 'article', collection: @articles, cached: true %>
<!-- 自定义间距 -->
<%= render partial: 'article', collection: @articles, spacer_template: 'divider' %>
<!-- app/views/articles/_divider.html.erb -->
<hr class="divider">
五、布局 #
5.1 布局文件 #
erb
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title><%= page_title(yield(:title)) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= render 'shared/header' %>
<main>
<%= yield %> <!-- 内容插入点 -->
</main>
<%= render 'shared/footer' %>
</body>
</html>
5.2 多个yield #
erb
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %></title>
<%= yield(:meta_tags) %>
</head>
<body>
<header>
<%= yield(:header) %>
</header>
<main>
<%= yield %> <!-- 默认内容 -->
</main>
<footer>
<%= yield(:footer) %>
</footer>
</body>
</html>
<!-- app/views/articles/show.html.erb -->
<% content_for :title, @article.title %>
<% content_for :meta_tags do %>
<meta name="description" content="<%= @article.summary %>">
<% end %>
<% content_for :header do %>
<h1><%= @article.title %></h1>
<% end %>
<article>
<%= @article.body %>
</article>
5.3 布局继承 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
layout 'application' # 默认布局
# 条件布局
layout :choose_layout
private
def choose_layout
if request.xhr?
false # 不使用布局
else
'application'
end
end
end
# 特定动作不使用布局
class ArticlesController < ApplicationController
layout 'application', except: [:rss, :atom]
end
六、辅助方法 #
6.1 链接辅助方法 #
erb
<!-- 基本链接 -->
<%= link_to '首页', root_path %>
<!-- 带HTML属性的链接 -->
<%= link_to '文章', articles_path, class: 'btn btn-primary', id: 'articles-link' %>
<!-- 带确认对话框 -->
<%= link_to '删除', article_path(@article),
data: { turbo_method: :delete, turbo_confirm: '确定删除?' } %>
<!-- 块链接 -->
<%= link_to article_path(@article) do %>
<strong><%= @article.title %></strong>
<span>点击查看</span>
<% end %>
<!-- 邮件链接 -->
<%= mail_to 'info@example.com', '联系我们' %>
<!-- 电话链接 -->
<%= phone_to '1234567890', '拨打电话' %>
6.2 表单辅助方法 #
erb
<!-- 基本表单 -->
<%= form_with model: @article do |form| %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title, class: 'form-control' %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body, rows: 10, class: 'form-control' %>
</div>
<div class="actions">
<%= form.submit '保存', class: 'btn btn-primary' %>
</div>
<% end %>
<!-- 表单选项 -->
<%= form.select :category_id, Category.pluck(:name, :id) %>
<%= form.select :status, [['草稿', 'draft'], ['发布', 'published']] %>
<%= form.collection_select :author_id, Author.all, :id, :name %>
6.3 资源辅助方法 #
erb
<!-- 样式表 -->
<%= stylesheet_link_tag 'application' %>
<%= stylesheet_link_tag 'print', media: 'print' %>
<!-- JavaScript -->
<%= javascript_importmap_tags %>
<%= javascript_include_tag 'application' %>
<!-- 图片 -->
<%= image_tag 'logo.png', alt: 'Logo' %>
<%= image_tag 'icon.png', size: '16x16' %>
<%= image_tag 'photo.jpg', class: 'img-fluid' %>
<!-- 视频和音频 -->
<%= video_tag 'intro.mp4', controls: true %>
<%= audio_tag 'music.mp3', controls: true %>
6.4 文本辅助方法 #
erb
<!-- 截断文本 -->
<%= truncate(@article.body, length: 100) %>
<%= truncate(@article.body, length: 100, omission: '...') %>
<!-- 高亮文本 -->
<%= highlight(@article.body, 'Rails') %>
<!-- 简单格式化 -->
<%= simple_format(@article.body) %>
<!-- 自动链接 -->
<%= auto_link(@article.body) %>
<!-- 复数化 -->
<%= pluralize(@articles.count, 'article') %>
6.5 数字辅助方法 #
erb
<!-- 格式化数字 -->
<%= number_with_delimiter(1234567) %> <!-- 1,234,567 -->
<%= number_with_precision(3.14159, precision: 2) %> <!-- 3.14 -->
<!-- 货币格式 -->
<%= number_to_currency(123.45) %> <!-- $123.45 -->
<%= number_to_currency(123.45, unit: '¥') %> <!-- ¥123.45 -->
<!-- 百分比格式 -->
<%= number_to_percentage(0.5, precision: 0) %> <!-- 50% -->
<!-- 文件大小 -->
<%= number_to_human_size(1234567) %> <!-- 1.18 MB -->
6.6 日期辅助方法 #
erb
<!-- 格式化日期 -->
<%= l Date.today, format: :long %> <!-- 2024年3月28日 -->
<%= l Time.now, format: :short %> <!-- 28日 15:30 -->
<!-- 距离时间 -->
<%= distance_of_time_in_words(Time.now, Time.now + 1.day) %> <!-- about 1 day -->
<!-- 时间标签 -->
<%= time_tag Date.today %> <!-- <time datetime="2024-03-28">2024-03-28</time> -->
七、调试辅助方法 #
7.1 debug #
erb
<!-- 格式化输出对象 -->
<%= debug(@article) %>
<!-- 输出YAML格式 -->
<%= simple_format(@article.to_yaml) %>
7.2 console #
erb
<!-- 在页面中打开控制台 -->
<%= console %>
八、最佳实践 #
8.1 视图保持简洁 #
erb
<!-- 不推荐:视图中包含复杂逻辑 -->
<% if @article.status == 'published' && @article.published_at <= Time.now && current_user.admin? %>
<span class="badge">管理员可见</span>
<% end %>
<!-- 推荐:使用辅助方法 -->
<%= admin_badge(@article) if show_admin_badge?(@article) %>
<!-- app/helpers/articles_helper.rb -->
module ArticlesHelper
def admin_badge(article)
content_tag :span, '管理员可见', class: 'badge'
end
def show_admin_badge?(article)
article.published? && current_user&.admin?
end
end
8.2 使用部分视图 #
erb
<!-- 不推荐:重复代码 -->
<% @articles.each do |article| %>
<div class="article">
<h2><%= article.title %></h2>
<p><%= truncate(article.body, length: 100) %></p>
<div class="meta">
<span>作者:<%= article.author.name %></span>
</div>
</div>
<% end %>
<!-- 推荐:使用部分视图 -->
<%= render @articles %>
8.3 避免N+1查询 #
erb
<!-- 不推荐:N+1查询 -->
<% @articles.each do |article| %>
<p><%= article.author.name %></p> <!-- 每次循环查询作者 -->
<% end %>
<!-- 推荐:预加载关联 -->
<!-- 控制器中 -->
@articles = Article.includes(:author).all
<!-- 视图中 -->
<% @articles.each do |article| %>
<p><%= article.author.name %></p> <!-- 不会额外查询 -->
<% end %>
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| ERB标签 | <% %>执行,<%= %>输出 |
| 部分视图 | 复用视图代码 |
| 布局 | 页面框架结构 |
| 辅助方法 | 简化视图代码 |
| HTML转义 | 防止XSS攻击 |
9.2 下一步 #
现在你已经掌握了ERB模板,接下来让我们学习 布局与渲染,深入了解Rails的布局系统!
最后更新:2026-03-28