ruby on rails h5/web 2套 theme
如何使用 rails 生成 h5/web 2套 theme
太好了!你选择的是 方案一:基于 User-Agent 自动判断设备类型,并加载两套完全独立的 View,这是一个非常清晰且可维护的方式,尤其适合 H5 和 Web 界面差异较大的场景。
下面我会为你详细规划整个 Rails 项目的结构、逻辑流程和最佳实践,确保两套视图完全独立、互不干扰,同时保持业务逻辑的 DRY(不重复)。
✅ 目标
- 同一个控制器处理逻辑
- 根据设备自动选择:
- H5 主题 → 使用
views/h5/...
下的视图 - Web 主题 → 使用
views/web/...
下的视图
- H5 主题 → 使用
- 布局、样式、视图全部独立
- 控制器复用业务逻辑(如查询数据)
🗂 项目结构规划
app/
├── controllers/
│ └── posts_controller.rb # 共用控制器
├── views/
│ ├── h5/ # H5 主题视图
│ │ ├── posts/
│ │ │ ├── index.html.erb
│ │ │ ├── show.html.erb
│ │ │ └── _post.html.erb
│ │ └── layouts/
│ │ └── application.html.erb
│ ├── web/ # Web 主题视图
│ │ ├── posts/
│ │ │ ├── index.html.erb
│ │ │ ├── show.html.erb
│ │ │ └── _post.html.erb
│ │ └── layouts/
│ │ └── application.html.erb
│ └── shared/ # 可选:共享 partial
│ └── _analytics.html.erb
├── helpers/
│ ├── h5_helper.rb
│ ├── web_helper.rb
│ └── application_helper.rb
└── assets/
├── stylesheets/
│ ├── h5.scss # @import "h5/**/*.scss"
│ ├── web.scss
│ ├── h5/
│ │ └── posts.scss
│ └── web/
│ └── posts.scss
└── javascript/
├── h5.js
└── web.js
🔧 步骤 1:添加 browser
gem(用于设备检测)
# Gemfile
gem 'browser', '~> 4.0'
bundle install
🔧 步骤 2:在 ApplicationController 中判断设备并设置视图路径
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_view_path
private
def set_view_path
if browser.device.mobile? || browser.device.tablet?
prepend_view_path 'app/views/h5'
@current_theme = :h5
else
prepend_view_path 'app/views/web'
@current_theme = :web
end
# 可选:存入 Thread 或实例变量供 helper 使用
@device_type = @current_theme
end
end
✅
prepend_view_path
是关键:它会优先查找h5/
或web/
目录下的视图。
🔧 步骤 3:创建两套独立的布局文件
H5 布局
<!-- app/views/h5/layouts/application.html.erb -->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H5主题 - <%= yield(:title) %></title>
<%= stylesheet_link_tag 'h5' %>
<%= javascript_include_tag 'h5' %>
<%= csrf_meta_tags %>
</head>
<body class="h5-body">
<header>📱 H5 Header</header>
<main><%= yield %></main>
<footer>© H5 Theme</footer>
</body>
</html>
Web 布局
<!-- app/views/web/layouts/application.html.erb -->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Web主题 - <%= yield(:title) %></title>
<%= stylesheet_link_tag 'web' %>
<%= javascript_include_tag 'web' %>
<%= csrf_meta_tags %>
</head>
<body class="web-body">
<header>🖥️ Web Header</header>
<nav>导航栏...</nav>
<main><%= yield %></main>
<footer>© Web Theme</footer>
</body>
</html>
🔧 步骤 4:创建控制器(复用逻辑)
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.published.order(created_at: :desc)
end
def show
@post = Post.find(params[:id])
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to post_path(@post), notice: '发布成功'
else
render :new
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
✅ 控制器无需关心视图,Rails 会自动去
h5/posts/
或web/posts/
找对应模板。
🔧 步骤 5:创建两套视图
H5 视图示例
<!-- app/views/h5/posts/index.html.erb -->
<div class="h5-post-list">
<% @posts.each do |post| %>
<a href="<%= post_path(post) %>">
<h3><%= post.title %></h3>
</a>
<% end %>
</div>
Web 视图示例
<!-- app/views/web/posts/index.html.erb -->
<div class="web-post-grid">
<h1>所有文章</h1>
<% @posts.each do |post| %>
<article class="card">
<h2><%= link_to post.title, post_path(post) %></h2>
<p><%= truncate(post.body, length: 100) %></p>
</article>
<% end %>
</div>
🔧 步骤 6:静态资源分离(CSS/JS)
编译独立的 CSS 和 JS
// app/assets/stylesheets/h5.scss
/*
*= require_self
*= require_tree ./h5
*/
// app/assets/stylesheets/web.scss
/*
*= require_self
*= require_tree ./web
*/
// app/assets/javascripts/h5.js
//= require_self
//= require_tree ./h5
console.log("H5 JS loaded");
// web.js 同理
🔧 步骤 7:Helper 分离(可选)
# app/helpers/h5_helper.rb
module H5Helper
def h5_button(text)
content_tag :button, text, class: 'h5-btn'
end
end
# app/helpers/web_helper.rb
module WebHelper
def web_button(text)
content_tag :button, text, class: 'web-btn primary'
end
end
在 layout 中自动加载(Rails 会根据 layout 自动 include 对应 helper)。
✅ 验证流程
- 用手机访问
/posts
→ 自动加载h5/posts/index.html.erb
+h5
布局 - 用电脑访问
/posts
→ 自动加载web/posts/index.html.erb
+web
布局 - 控制器逻辑完全复用
- 样式和脚本独立加载
🧪 测试设备判断(开发时)
你可以临时伪造 User-Agent 测试:
方法 1:浏览器插件
- 安装 "User-Agent Switcher" 插件(Chrome/Firefox)
- 切换为 iPhone 或 Android User-Agent
方法 2:curl 测试
curl -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)" http://localhost:3000/posts
✅ 优点总结
| 优势 | 说明 |
|------|------|
| ✅ 完全独立视图 | H5 和 Web 可以完全不同,互不影响 |
| ✅ 逻辑复用 | 控制器、模型、helpers 可共享 |
| ✅ 易维护 | 结构清晰,按主题组织 |
| ✅ 自动适配 | 用户无感知,体验好 |
🚀 进阶建议
- 加入
session[:force_theme]
支持用户手动覆盖(如“切换到电脑版”链接) - 使用
respond_to
+format.mobile?
(需responders
gem)作为替代方案 - 如果用 Rails API + 前端框架,建议拆分为两个前端项目(React/Vue H5 vs Web)
需要我生成一个最小可运行的 GitHub 项目结构或代码模板吗?可以一键克隆的那种 😄
欢迎继续提问!