CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Ruby on Rails 实战入门/课时/视图与组件
RawBlame
课时目录12 节

Ruby on Rails 实战入门

  1. 01Rails 概览与项目结构
  2. 02路由设计
  3. 03控制器
  4. 04Active Record 模型
  5. 05查询与优化
  6. 06数据库适配
  7. 07视图与组件
  8. 08表单
  9. 09认证与授权
  10. 10资产与后台任务
  11. 11测试与调试
  12. 12部署与运维

视图与组件

ERB、ViewComponent 与 Hotwire。

30 分钟

视图与组件

这一课你将能做到

  1. 用 ERB 模板渲染数据,理解 <%= %> 与 <% %> 的区别
  2. 用布局 + yield + content_for 组织页面骨架,用 partial 消除重复片段
  3. 说清楚 Rails 服务端渲染与 Next.js 客户端渲染的取舍,知道 Hotwire 补位交互

为什么要学

视图是用户唯一能看见的部分。Rails 的视图层哲学是「HTML 优先」:服务端把数据填进模板直接吐出完整 HTML,浏览器零 JS 也能用;需要交互的地方交给 Hotwire(Turbo + Stimulus)做渐进增强。这和本站的 Next.js 路线(浏览器下载 JS bundle → 客户端取数据渲染)是两种范式,各自有明确的适用边界——内容型、后台型产品服务端渲染交付更快,重交互应用才需要前端框架。

先建立图像

视图的套娃结构:

layouts/application.html.erb          ← 最外层:html/head/body 骨架
  └── yield                           ← 内容插槽
        └── articles/index.html.erb   ← 动作对应视图
              └── render "article"    ← partial 局部视图(每个文章卡片)
                    └── _article.html.erb

渲染顺序由内而外:partial → 动作视图 → 布局 → 完整 HTML 响应。

ERB 两种标签:

<% @articles.each do |a| %>   ← 只执行,不输出
  <%= a.title %>              ← 执行并输出(HTML 转义)
<% end %>

跟着做

步骤 1:第一个视图

app/views/articles/index.html.erb:

<h1>文章列表</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article_path(article) %>
      <small><%= article.created_at.strftime("%Y-%m-%d") %></small>
    </li>
  <% end %>
</ul>

<%= link_to "写新文章", new_article_path %>

控制器里 @articles = Article.all,视图直接用实例变量。文件名规则:动作名.格式.模板引擎——index.html.erb 意为「HTML 格式、ERB 引擎」。

步骤 2:布局与 yield

app/views/layouts/application.html.erb:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title><%= content_for(:title) || "我的博客" %></title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_importmap_tags %>
</head>
<body>
  <nav><%= link_to "首页", root_path %></nav>

  <% flash.each do |type, msg| %>
    <div class="flash <%= type %>"><%= msg %></div>
  <% end %>

  <main><%= yield %></main>

  <footer>© 2026</footer>
</body>
</html>

每个动作视图的内容会被塞进 yield 位置。子页面想定制标题:

<%# app/views/articles/show.html.erb %>
<% content_for :title, @article.title %>
<h1><%= @article.title %></h1>

csrf_meta_tags 输出 CSRF 令牌给 JS 用,stylesheet_link_tag/javascript_importmap_tags 挂资产——都是布局的标配。

步骤 3:partial 消除重复

列表页和搜索页都要渲染文章卡片,抽成 partial。文件名以下划线开头:

<%# app/views/articles/_article.html.erb %>
<article class="card">
  <h2><%= link_to article.title, article_path(article) %></h2>
  <p><%= truncate(article.body, length: 120) %></p>
</article>

使用:

<%# 单条渲染 %>
<%= render "article", article: @article %>

<%# 集合渲染:自动对每条记录调用一次 %>
<%= render @articles %>

render @articles 是 Rails 的约定魔法:按模型类名找到 _article.html.erb,循环渲染并把局部变量 article 依次赋值——等价于手写 each + render,但短得多。

步骤 4:helper 放视图逻辑

视图中不该有复杂 Ruby 逻辑,抽到 app/helpers/articles_helper.rb:

module ArticlesHelper
  def status_badge(article)
    if article.published?
      tag.span "已发布", class: "badge badge-green"
    else
      tag.span "草稿", class: "badge badge-gray"
    end
  end
end

视图里一行调用:<%= status_badge(article) %>。tag.span 是内置的 HTML 生成器,比手写字符串安全(自动转义属性)。

步骤 5:了解组件化方向——ViewComponent

当 partial + helper 的组合在大型项目里失控(隐式依赖实例变量、难以测试),社区方案 ViewComponent(GitHub 开源)把视图封装成 Ruby 类:

# app/components/article_card_component.rb
class ArticleCardComponent < ViewComponent::Base
  def initialize(article:)
    @article = article
  end
end
<%# app/components/article_card_component.html.erb %>
<article class="card">
  <h2><%= link_to @article.title, article_path(@article) %></h2>
</article>

调用:<%= render ArticleCardComponent.new(article: @article) %>。显式传参、可单测——思路和 React 组件一致,但渲染发生在服务端。初学者先把 partial 用熟,项目大了再上组件库。

讲清楚要点

  1. <%= %> 输出并自动转义:<%= "<b>x</b>" %> 会显示成文本 <b>x</b> 而不是加粗——这是 XSS 防线。确需输出可信 HTML 用 raw() 或 .html_safe,但永远不要对用户输入这么做。
  2. 实例变量是控制器到视图的桥:@articles 在控制器赋值,视图直接可用。partial 里用局部变量(render 时显式传),别依赖实例变量——隐式依赖让 partial 无法复用。
  3. 布局可多套:layout "admin" 切换布局;render layout: false 不要布局(如弹窗片段)。
  4. helper 自动可用:ArticlesHelper 的方法在 articles 相关视图直接调用。所有 helper 默认全局可见,命名要清晰避免冲突。
  5. Hotwire 是视图层的交互答案:Turbo 把链接/表单提交变成局部替换(无刷新体验),Stimulus 是轻量 JS 控制器处理富交互。80% 的「想要 SPA 感」需求,Hotwire 就够,不必上 React。

你可能会懵的地方

  • ERB 和其他模板引擎(Haml/Slim)选哪个? ERB 是官方默认、文档最全、团队零学习成本。Haml/Slim 用缩进代替闭合标签,写起来快但可读性见仁见智。初学者用 ERB,别折腾。

  • render 和 render partial: 区别? render "article" 在视图里默认就是渲染 partial(找下划线文件);render template: "articles/special" 才渲染完整模板。日常 99% 是 partial。

  • 集合渲染怎么做分页? 分页是查询层的事:控制器里 @articles = Article.page(params[:page]).per(20)(kaminari 或 pagy gem),视图 <%= render @articles %> 照旧,加分页导航 <%= paginate @articles %>。

  • 视图里能直接查数据库吗? 语法上可以(<% Article.count %>),但这是严重坏味道——视图查询绕过控制器,既难缓存又容易 N+1。所有数据在控制器备好。

  • 什么时候该用 Next.js 而不是 Rails 视图? 交互密度是分水岭:拖拽编辑器、实时协作、复杂状态机——Next.js;CRUD、内容展示、表单流程、后台管理——Rails 视图 + Hotwire 更快更省。很多产品是混合架构:对外的营销页 Rails 渲染,核心交互应用 Next.js。

错法对照

错法问题正解
<%= raw(comment.body) %> 渲染用户输入XSS 漏洞默认 <%= %> 自动转义,富文本用 sanitize
partial 里直接用 @article隐式依赖,别处无法复用render "article", article: a 显式传局部变量
视图里 <% Article.where... %>绕过控制器,易 N+1数据在控制器查询
五层嵌套 if/else 在 ERB 里逻辑混乱难测抽 helper 或 presenter
所有页面共用一套布局硬编码后台/前台风格冲突多布局 + layout "admin"

卡点急救

  1. ActionView::MissingTemplate:文件名不符合 动作.格式.引擎 约定,或放错目录(应在 app/views/控制器名/ 下)。检查单复数。
  2. partial 报 undefined local variable or method 'article':渲染时没传局部变量,补 render "article", article: @article;集合渲染确认模型类名与 partial 文件名匹配。
  3. 修改视图不生效:生产/预编译环境模板有缓存,开发环境正常应即时生效;若在开发环境不生效,重启服务器并检查是否改对了环境对应的文件。

面试常问

  • Rails 服务端渲染和 React/Next.js 客户端渲染怎么选? 答:看交互密度和团队结构。内容/CRUD/后台类产品,Rails 服务端渲染 + Hotwire 交付快、SEO 天然友好、一套代码无 API 层;重交互(编辑器、实时协作)、多端复用数据、前端团队独立时,前后端分离更合适。成本模型:前者省一层 API 和状态同步,后者换来交互上限和团队并行。

  • ERB 的 <%= %> 为什么默认转义? 答:防 XSS。用户输入若含 <script>,转义后显示为文本而非执行。输出可信 HTML 需显式 raw/sanitize——把「危险操作」做成显式动作,是 Rails 的安全设计哲学。

  • partial 和 ViewComponent 的取舍? 答:partial 轻量、约定式,适合页面片段复用;但依赖隐式约定、无法独立测试。ViewComponent 显式传参、有 Ruby 类可单测、支持插槽,适合设计系统和大型项目。小项目 partial 足够,组件超过几十个考虑迁移。

自测三问

  1. <% %> 和 <%= %> 的区别?后者为什么天然防 XSS?
  2. <%= render @articles %> 背后发生了什么约定推断?
  3. 布局里的 yield 和 content_for 分别解决什么问题?

小结一句话

Rails 视图 = 布局套模板、模板嵌 partial,默认转义保安全,数据永远来自控制器——HTML 优先,交互交给 Hotwire 增强。

延伸

  • Action View Overview(官方视图指南)
  • 下一课:表单——form_with、Strong Parameters 与文件上传。
加载本节练习…
← 上一课数据库适配下一课 →表单