Ruby on Rails 视图(Views)与 ERB 模板:从布局、局部模板到辅助方法的完整实战指南
【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum
视图(View)是 Rails MVC 架构中面向用户的一层:它本质上是一份嵌入了 Ruby 代码的 HTML 模板,接收控制器传来的实例变量并渲染成最终网页。本文以《Ruby on Rails 课程》的 views.md 为核心,系统讲解视图模板的组织方式、布局(Layout)与yield、ERB 预处理器、局部模板(Partial)及其传参技巧、link_to与 Asset 标签等辅助方法,并结合仓库中 controllers.md、routing.md、asset_pipeline.md 等关联课程与项目文档做源码级佐证。学完本文,你将能独立规划一个 Rails 应用的视图目录、编写动态 ERB 模板、用局部模板消除重复代码,并正确使用 Rails 提供的视图辅助方法。
视图在 MVC 中的定位与目录约定
在 MVC 结构中,视图(View)是相对最简单的一层。从基本层面看,它就是一份 HTML 骨架(boilerplate),在其中插入从控制器接收到的变量,最终发送给浏览器——这就是你实际看到的"网页"。视图里通常会包含少量用来呈现这些变量的代码片段,例如一个遍历博客所有帖子的循环。因此,视图也常被称为"视图模板"(view template)。
Rails 通过一套严格的目录与命名约定来组织视图:
app/views/controller_name/action_name.html.erb其中controller_name是与视图关联的控制器名,action_name.html.erb对应控制器中在渲染该视图之前刚刚执行的那个方法(action)。
例如,Posts控制器执行#index动作后,会隐式渲染app/views/posts/index.html.erb。你也可以在控制器中通过给render函数传参,显式地告诉控制器渲染其他名字的视图,但通常没有必要——这套目录和命名结构能让你(和 Rails)始终知道某个视图在哪里。这一点在 controllers.md 的"Naming matters"一节中被进一步强调:控制器动作结束后,Rails 会收集该动作中设置的所有实例变量,送往与动作同名、且位于以控制器命名的文件夹中的视图文件(如app/views/posts/index.html.erb)。这不是随意的,而是刻意为之,方便你日后查找文件;如果你把文件放在其他目录或层级,就必须显式指定要渲染哪个视图。
在视图中使用来自控制器的实例变量,方式与控制器中完全一致:直接写@user.first_name、@posts或@some_other_variable即可。
布局(Layouts):整站页面的外壳
需要首先注意的一点是:从控制器渲染出来的命名视图模板其实并不是完整的网页。它不包含<head>标签、DOCTYPE声明,也不包含那些在所有页面中都会出现的基础结构。正因为这些内容存在于每个页面中,Rails 的设计者很聪明地把它们抽成了独立文件,称为"布局"(Layout),存放于app/views/layouts目录。
对于一个全新创建的 Rails 应用,application.html.erb布局非常基础:它包含所有网页都需要的基础标签(如<html>和<body>),以及一小段加载网页所需的 JavaScript 和 CSS 文件的代码。任何需要在所有页面中共享的内容都应放进布局,通常包括:
- 导航栏(navbar)和页脚(footer);
- 用于展示 flash 消息(如登录成功提示)的代码片段。
那么,既然布局只是包裹单个页面的"外壳",页面内容是如何插入进去的?这就回到了 Ruby 的yield方法(你在学习块/block 时见过它)。视图模板app/views/posts/index.html.erb会在布局中yield语句所在的位置被插入。随着你水平提高,还能灵活操纵这条语句(例如用多个命名yield实现多区块布局)。
预处理器与 ERB:在 HTML 中嵌入 Ruby
你在视图文件里无疑会注意到<%=与%>之间那段"奇怪的 HTML"。这就是嵌入式 Ruby(Embedded Ruby,简称 ERB)——一种在 HTML 内部执行 Ruby 代码的特殊方式。HTML 是静态的,而一旦你想做循环、if判断或处理变量这类动态事情,就需要引入 Ruby。ERB(以及你可能见过的类似语言 HAML)正是为此而生。
<%、<%=与<%#的区别
这些标签的作用是:把标签内的内容当作普通 Ruby 代码原样执行。
<%= ... %>:执行标签内的代码,并显示其返回值。例如<em><%= "I am emphasized" %></em>会输出强调文本I am emphasized,<%= @user.first_name %>可能输出joe。<% ... %>:执行代码,但无论这行返回什么,都不会在 HTML 模板中显示任何内容。<%# ... %>:注释,不会执行。
大多数标签你会用<%=,因为你经常要输出从控制器收到的实例变量的关键部分。而<%用于纯粹代码性质的语句,比如if判断和each循环——这些地方你并不希望直接显示什么(要显示的内容写在循环体内部)。
一个完整的 ERB 示例
假设我们要显示应用中所有用户的名字,但仅当当前用户已登录时才显示:
<% if current_user.signed_in? %> <ul> <% @users.each do |user| %> <li><%= user.first_name %></li> <% end %> </ul> <% else %> <strong>You must sign in!</strong> <% end %>别忘了用<% end %>关闭你的语句和循环!(你一定会忘掉几次。)
当用户已登录时,上面代码实际渲染到网页的内容是:
<ul> <li>Bob</li> <li>Joe</li> <li>Nancy</li> </ul>当用户未登录时,则只有:
<strong>You must sign in!</strong>一个新手常见错误:如果在循环行误用了<%=,比如<%= @users.each do |user| %>,代码仍能正常运行,但由于each返回的是原始集合,页面上会顺带把整个@users变量倾倒出来(非常不专业)。这种错误你多半会犯上几次,然后很快就学会了。
预处理器如何工作:扩展名从外到内
上述代码执行的一个关键点是:这一切都发生在服务器端,在最终 HTML 文件被发送到浏览器之前(属于 Asset Pipeline 的一部分,见 asset_pipeline.md)。因为当你渲染模板时,Rails 会先运行 ERB 这类"预处理器"(preprocessor)。它之所以知道要预处理该文件,是因为文件扩展名是.html.erb。
Rails 从外向内处理额外的扩展名:先按 ERB 处理文件,再将其当作普通 HTML。这样没问题,因为 ERB 按其定义输出的是干净合法的 HTML(如上例所示)。
你还会遇到其他预处理器:
.css.scss文件使用 SASS 预处理器,最终变成普通 CSS;.js.coffee文件使用 CoffeeScript 预处理器,运行后变成普通 JavaScript。
在这两种情况下,预处理器语言都通过提供额外工具(比如循环和变量)让你的工作更轻松,然后编译回朴素的 CSS、JavaScript 或 HTML。
要点是:预处理器种类繁多,通常是 Rails 自带或可以轻松挂载的 gem。Rails 会自动运行它们,所以你唯一要关心的是文件是否有正确的扩展名来触发对应的预处理器。
视图局部模板(Partials):复用与拆分
Rails 还允许你把视图拆分成"局部模板"(partial)。这能在多个层面帮助你:让代码更简洁、更易读,同时允许你复用某些常见模式。一个典型例子是创建或编辑用户的表单:#new和#edit两个动作都需要渲染某种用户表单,而且通常几乎一模一样。于是人们常把这个表单放进一个名为_user_form.html.erb的新文件,然后在new.html.erb和edit.html.erb视图模板中按需调用它。
退一步说,局部模板就是那些不打算自成一体、但可以被其他文件共享的 HTML 文件。调用局部模板的写法如下:
# app/views/users/new.html.erb <div class="new-user-form"> <%= render "user_form" %> </div>这里有几个语法细节需要注意:
- 局部模板文件以下划线开头命名,如
_user_form.html.erb,但调用时只用名字的核心部分,如上面的user_form。 - 如果局部模板的名字没有指定目录,Rails 只会查找调用它的视图所在文件夹(例如
app/views/users)。 - 有时你会希望跨多个控制器的视图模板共享局部模板,这时可以把它们保存在专门的
app/views/shared文件夹中,并用<%= render "shared/some_partial" %>来渲染。
向局部模板传递局部变量
局部模板能访问调用它的视图模板所拥有的所有变量,但不要依赖这一点!如果这个局部模板被另一个使用不同实例变量结构的控制器调用怎么办?指望@user这样的实例变量始终存在于局部模板中,是糟糕的代码。这意味着你必须显式地把局部模板需要访问的变量传给它。
render只是一个普通方法,允许你传入一个 options 哈希,其中一个选项是:locals键,包含你想传递的变量。在上面的例子中,你很可能想把@user传给局部模板,以便渲染正确类型的表单:
<%= render partial: "shared/your_partial", :locals => { :user => user } %>在局部模板文件中使用该变量时,去掉@,像普通变量一样调用。注意:用:partial键调用render方法时,应该配合:locals选项。
render还有一个快捷写法,无需:locals选项即可传参:
<%= render "shared/your_partial", :user => user %>隐式局部模板:渲染模型的魔法捷径
和往常一样,某些操作你做得太频繁,Rails 就给了你捷径——其中之一就是渲染 User 或 Post 这样的模型对象。
如果你想列出所有用户,可以在app/views/users/index.html.erb中反复手写展示单个用户姓、名、邮箱等的 HTML 和 ERB 代码,或者把它们放进某个each循环。但通常最好把 User 做成独立的_user.html.erb局部模板,以便在其他场景复用。基础调用方式与我们上面看到的类似:
# app/views/index.html.erb <h1>Users</h1> <ul> <% @users.each do |user| %> <%= render "user", :locals => {:user => user} %> <% end %> </ul>而在局部模板中:
# app/views/_user.html.erb <li><%= "#{user.first_name} #{user.last_name}, #{user.email}" %></li>一个局部模板只有一行可能看起来很奇怪,但它通常不会一直这么短,所以值得掌握这种写法。
如果这是基础方式,那"魔法般"的 Rails 方式是什么?直接告诉它渲染 User 对象即可:
# app/views/index.html.erb <h1>Users</h1> <ul> <% @users.each do |user| %> <%= render user %> <!-- 代码少得多 --> <% end %> </ul>Rails 会在当前目录查找_user.html.erb文件,并自动把user变量传给它。
如果你想渲染一整批用户呢?Rails 同样帮你做好了,省去手写each循环的麻烦:
# app/views/index.html.erb <h1>Users</h1> <ul> <%= render @users %> </ul>在这种情况下,Rails 不仅会找到_user.html.erb文件并传入正确的user变量,还会替你遍历整个@user集合。非常方便。
辅助方法(Helper Methods):视图中的得力工具
render局部模板并不是视图中唯一可调用的方法。Rails 有一大批非常实用的辅助方法可供在视图中使用。以下是几个最常见的:
#link_to:动态生成锚点链接
link_to用于创建锚点标签(URL)。与其手写:
<a href="<%= users_path %>">See All Users</a>不如写成:
<%= link_to "See All Users", users_path %>这就是 Rails 风格。回忆一下路由课程(routing.md)中的内容:users_path生成相对 URL,如/users;而users_url生成完整 URL,如http://www.yourapp.com/users。多数情况下这个区别不重要,因为浏览器都能处理,但请务必理解两者的差异。
为什么不硬编码 URL?正如 routing.md 所强调的:当你决定修改 URL 时,硬编码会让你不得不手动逐个修改页面。Rails 会自动为所有路由生成以_path和_url结尾的辅助方法,需要指定 ID 或参数的路由还要把参数传给这些方法,例如link_to "Edit this post", edit_post_path(3)会生成/posts/3/edit;还能附加查询字符串,如post_path(3, :referral_link => "/some/path"),该参数会出现在控制器的params哈希中。
Asset 标签:引入 CSS、JavaScript 与图片
正如你在应用布局文件中看到的,Rails 提供了输出 HTML 标签以获取 CSS 或 JavaScript 文件的辅助方法,也可以获取图片。这些方法统称为 Asset 标签(Asset Tags)。关于"Asset Pipeline"会在后续课程深入讲解,这里先记住:这些标签根据文件名帮你定位文件并渲染出正确的 HTML 标签。
<%= stylesheet_link_tag "your_stylesheet" %> <%= javascript_include_tag "your_javascript" %> <%= image_tag "happy_cat.jpg" %>会渲染成类似:
<link href="/assets/your_stylesheet.css" media="all" rel="stylesheet"> <script src="/assets/your_javascript.js"></script> <img src="/assets/happy_cat.jpg">注意:在生产环境中,你的样式表和 JavaScript 会被合并成一个带奇怪名字的文件,所以看到类似/assets/application-485ea683b962efeaa58dd8e32925dadf的名字不要惊慌。这正是 asset_pipeline.md 中讲的"指纹"(fingerprinting)机制——Rails 给资源文件名加上唯一标识以辅助浏览器缓存:内容一变,指纹就变,浏览器发现新 URL 不在缓存中便会重新获取。这一指纹由 Rails 的 link helpers 自动提供,例如布局<head>中的<%= stylesheet_link_tag "application" %>即可享受该能力。
表单辅助方法
Rails 提供了多个辅助方法来创建表单,将在后续课程中深入讲解。提前预告:核心是form_with辅助方法,它不仅能自动生成 CSRF 安全令牌(authenticity_token隐藏输入),还能在传入模型对象时智能判断提交目标——新对象提交到#create动作,已保存对象提交到#update动作;校验失败时它还会自动用带field_with_errors类的<div>包裹出错字段。完整的表单知识可继续学习 form_basics.md 与实战项目 project_forms.md。
知识检查
回顾本课的核心知识点,检验你的掌握程度:
- 如何确保预处理器运行在你的视图文件上?——通过正确的文件扩展名(如
.html.erb、.css.scss、.js.coffee),Rails 从外向内自动执行对应的预处理器。 <%、<%=和<%#之间有什么区别?——<%=显示返回值;<%只执行不显示;<%#是注释,不执行。- 在布局中包含
<%= yield %>的作用是什么?——把控制器渲染的视图模板插入到布局中该语句所在的位置,构成完整页面。 - 为什么要用局部模板?——让代码更简洁易读,并能跨视图复用常见模式(如表单、列表项)。
- 渲染一个集合作为一系列局部模板的捷径是什么?——直接
<%= render @users %>,Rails 会自动遍历集合并渲染_user.html.erb。 - 如何在 Rails 应用中动态链接到另一个页面?——使用
link_to配合路由辅助方法,如link_to "See All Users", users_path。
结语
视图构成了应用面向用户的一面。一开始,你可能很难想象如何选择渲染哪个视图、视图里该放什么、以及如何使用局部模板——但只要跟着 Rails 的约定做几次迭代,你很快就能掌握这些规则。视图对你而言会成为如同呼吸般自然的东西。想深入了解更多细节(比如render的各种选项、布局中的多重yield、集合渲染的间隔模板等),可以继续研读 Rails 官方 Guides 中关于 Layouts and Rendering 的章节,并对照本仓库中 controllers.md(理解render与redirect_to的选择)与 asset_pipeline.md(理解资源如何被预处理与指纹化)串联起完整的请求-渲染链路。
【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考