Rails布局与渲染 #
一、布局概述 #
1.1 什么是布局 #
布局是包裹视图内容的模板框架,定义了页面的公共结构(头部、导航、页脚等)。
1.2 布局查找顺序 #
Rails按以下顺序查找布局:
app/views/layouts/[控制器名].html.erbapp/views/layouts/application.html.erb
二、布局基础 #
2.1 默认布局 #
erb
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>MyApp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
2.2 控制器特定布局 #
erb
<!-- app/views/layouts/articles.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>文章 - MyApp</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag "application" %>
</head>
<body>
<nav>
<%= link_to '文章列表', articles_path %>
</nav>
<%= yield %>
</body>
</html>
2.3 指定布局 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
layout 'admin' # 使用 admin.html.erb 布局
# 条件布局
layout :determine_layout
private
def determine_layout
if current_user&.admin?
'admin'
else
'application'
end
end
end
2.4 禁用布局 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
layout false # 禁用所有布局
# 特定动作禁用布局
layout false, only: [:rss, :atom]
end
三、yield和content_for #
3.1 基本yield #
erb
<!-- app/views/layouts/application.html.erb -->
<body>
<header>
<%= render 'shared/header' %>
</header>
<main>
<%= yield %> <!-- 视图内容插入点 -->
</main>
<footer>
<%= render 'shared/footer' %>
</footer>
</body>
3.2 命名yield #
erb
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) || 'MyApp' %></title>
<%= yield(:meta_tags) %>
<%= yield(:stylesheets) %>
</head>
<body>
<header>
<%= yield(:header) %>
<%= render 'shared/nav' %>
</header>
<main>
<%= yield %> <!-- 默认内容 -->
</main>
<aside>
<%= yield(:sidebar) %>
</aside>
<footer>
<%= yield(:footer) %>
<%= render 'shared/footer' %>
</footer>
<%= yield(:javascripts) %>
</body>
</html>
3.3 content_for #
erb
<!-- app/views/articles/show.html.erb -->
<% content_for :title, @article.title %>
<% content_for :meta_tags do %>
<meta name="description" content="<%= @article.summary %>">
<meta name="keywords" content="<%= @article.tags.join(', ') %>">
<% end %>
<% content_for :stylesheets do %>
<%= stylesheet_link_tag 'articles' %>
<% end %>
<% content_for :sidebar do %>
<div class="sidebar">
<h3>相关文章</h3>
<%= render 'related_articles' %>
</div>
<% end %>
<article>
<h1><%= @article.title %></h1>
<div class="content">
<%= @article.body %>
</div>
</article>
<% content_for :javascripts do %>
<%= javascript_include_tag 'articles' %>
<% end %>
3.4 content_for?检查 #
erb
<!-- app/views/layouts/application.html.erb -->
<head>
<title><%= yield(:title) || 'MyApp' %></title>
<% if content_for?(:meta_tags) %>
<%= yield(:meta_tags) %>
<% else %>
<meta name="description" content="默认描述">
<% end %>
</head>
四、渲染选项 #
4.1 渲染动作 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
# 渲染当前控制器的show视图
render :show
# 渲染同一控制器的其他动作视图
render :index
# 渲染其他控制器的视图
render 'users/profile'
# 渲染绝对路径
render file: Rails.root.join('public', '404.html')
end
end
4.2 渲染选项 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
# 渲染指定模板
render template: 'articles/special'
# 渲染内联模板
render inline: '<% @articles.each do |a| %><p><%= a.title %></p><% end %>'
# 渲染纯文本
render plain: 'Hello World'
# 渲染HTML
render html: '<h1>Hello</h1>'.html_safe
# 渲染JSON
render json: @article
# 渲染XML
render xml: @article
# 渲染JavaScript
render js: "alert('Hello')"
# 渲染原始内容
render body: 'raw body content'
end
end
4.3 渲染状态 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: '创建成功'
else
render :new, status: :unprocessable_entity
end
end
end
4.4 渲染布局选项 #
ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
# 使用特定布局
render :show, layout: 'print'
# 不使用布局
render :show, layout: false
# 使用布局并传递局部变量
render :show, layout: 'application', locals: { show_sidebar: true }
end
end
五、布局继承 #
5.1 基本继承 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout 'application'
end
# app/controllers/admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController
layout 'admin'
end
end
# app/controllers/admin/users_controller.rb
module Admin
class UsersController < Admin::ApplicationController
# 使用 admin 布局
end
end
5.2 布局层级 #
text
app/views/layouts/
├── application.html.erb # 基础布局
├── admin.html.erb # 管理后台布局
├── admin/
│ └── users.html.erb # 管理后台用户布局
└── devise.html.erb # Devise布局
六、模板查找 #
6.1 查找路径 #
Rails在以下路径查找模板:
text
app/views/
├── articles/
│ ├── index.html.erb
│ ├── show.html.erb
│ ├── _article.html.erb
│ └── _form.html.erb
├── shared/
│ ├── _header.html.erb
│ └── _footer.html.erb
└── layouts/
├── application.html.erb
└── admin.html.erb
6.2 自定义查找路径 #
ruby
# config/application.rb
module MyApp
class Application < Rails::Application
# 添加视图查找路径
config.paths['app/views'].unshift Rails.root.join('app', 'themes', 'default', 'views')
end
end
七、渲染集合 #
7.1 渲染集合 #
erb
<!-- 渲染集合 -->
<%= render @articles %>
<!-- 等价于 -->
<%= render partial: 'article', collection: @articles %>
<!-- 自定义局部变量名 -->
<%= render partial: 'article', collection: @articles, as: :item %>
7.2 集合渲染优化 #
erb
<!-- 使用缓存 -->
<%= render partial: 'article', collection: @articles, cached: true %>
<!-- 添加间距模板 -->
<%= render partial: 'article', collection: @articles, spacer_template: 'divider' %>
<!-- 空集合处理 -->
<%= render partial: 'article', collection: @articles || '暂无文章' %>
7.3 部分视图 #
erb
<!-- app/views/articles/_article.html.erb -->
<div class="article" id="article-<%= article.id %>">
<h2><%= link_to article.title, article %></h2>
<p><%= truncate(article.body, length: 100) %></p>
<div class="meta">
<span><%= article.author.name %></span>
<span><%= l article.created_at, format: :short %></span>
</div>
</div>
八、高级布局技巧 #
8.1 嵌套布局 #
erb
<!-- app/views/layouts/admin.html.erb -->
<% content_for :stylesheets do %>
<%= stylesheet_link_tag 'admin' %>
<% end %>
<% content_for :sidebar do %>
<nav class="admin-nav">
<%= render 'admin/shared/nav' %>
</nav>
<% end %>
<%= render template: 'layouts/application' %>
8.2 响应式布局 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout :layout_by_resource
private
def layout_by_resource
if devise_controller? && resource_name == :user && action_name == 'login'
'devise'
else
'application'
end
end
end
8.3 动态布局 #
ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout :choose_layout
private
def choose_layout
case
when request.xhr?
false
when request.format.json?
false
when current_user&.admin?
'admin'
when mobile_device?
'mobile'
else
'application'
end
end
def mobile_device?
request.user_agent =~ /Mobile|webOS/
end
end
九、最佳实践 #
9.1 布局组织 #
text
app/views/layouts/
├── application.html.erb # 主布局
├── admin.html.erb # 管理后台布局
├── devise.html.erb # 认证布局
├── mailer.html.erb # 邮件布局
├── pdf.html.erb # PDF布局
└── shared/
├── _head.html.erb # 头部共享部分
├── _nav.html.erb # 导航共享部分
└── _footer.html.erb # 页脚共享部分
9.2 内容组织 #
erb
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<%= render 'layouts/shared/head' %>
</head>
<body>
<%= render 'layouts/shared/nav' %>
<main>
<%= render 'shared/flash' %>
<%= yield %>
</main>
<%= render 'layouts/shared/footer' %>
<%= render 'layouts/shared/javascripts' %>
</body>
</html>
9.3 性能优化 #
erb
<!-- 使用缓存 -->
<% cache @article do %>
<%= render @article %>
<% end %>
<!-- 集合缓存 -->
<%= render partial: 'article', collection: @articles, cached: true %>
<!-- 条件渲染 -->
<%= render 'sidebar' if show_sidebar? %>
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 布局查找 | 控制器名 -> application |
| yield | 内容插入点 |
| content_for | 定义命名内容 |
| 渲染选项 | 多种渲染方式 |
| 布局继承 | 控制器继承链 |
10.2 下一步 #
现在你已经掌握了布局与渲染,接下来让我们学习 表单构建器,深入了解Rails的表单系统!
最后更新:2026-03-28