Rails 概览与项目结构
MVC、生成器与目录结构。
Rails 概览与项目结构
这一课你将能做到
- 用一条命令创建 Rails 7 应用并启动,浏览器访问到欢迎页
- 说清楚 MVC 三层在一次请求中的完整流转过程
- 解释 Gemfile、config、db、app 四大目录的职责,不看文档也能找到文件
为什么要学
Rails 是「约定优于配置」(Convention over Configuration)的代表框架。学它不是为了背 API,而是理解一种极致的工程哲学:框架替你做 80% 的决定,你只写剩下 20% 的业务代码。
和本站的技术栈对比最直观:本站用 Spring Boot(后端 API)+ Next.js(前端渲染),前后端是两个工程、两套部署。而 Rails 默认是「全栈一体」——一个工程里同时完成数据库读写、业务逻辑、HTML 渲染,一个人就能撑起一个完整产品。Basecamp、GitHub 早期、Shopify 都是这么跑起来的。理解这两种架构的差异,是你做技术选型时的底层判断力。
先建立图像
一次 HTTP 请求在 Rails 里的完整旅程:
浏览器 GET /articles/1
│
▼
┌─────────────┐ 匹配 /articles/:id
│ Router │ ──────────────────► ArticlesController#show
└─────────────┘
│
▼
┌──────────────┐ @article = Article.find(1)
│ Controller │ ──────────────────► 查数据库(Model 层)
└──────────────┘
│
▼
┌──────────────┐ 把 @article 填进模板
│ View │ ──────────────────► 生成 HTML 字符串
│ (ERB 模板) │
└──────────────┘
│
▼
浏览器收到完整 HTML 页面
对应到 Spring Boot 的话:Router ≈ @RequestMapping,Controller ≈ @RestController,Model ≈ JPA Entity,View 这层在前后端分离架构里被 Next.js 接管了。
跟着做
步骤 1:安装环境并创建应用
确保已安装 Ruby 3.2+ 和 SQLite3,然后:
# 安装 Rails 7.x
gem install rails -v 7.1
# 创建应用(blog 是经典教学项目名)
rails new blog
cd blog
# 启动开发服务器
bin/rails server
打开浏览器访问 http://localhost:3000,看到 Rails 欢迎页(Yay! You're on Rails!)就说明环境通了。
步骤 2:看懂生成的目录结构
blog/
├── app/ # 90% 的代码写在这里
│ ├── controllers/ # 控制器:接请求、调模型、挑视图
│ ├── models/ # 模型:业务逻辑 + 数据库映射
│ ├── views/ # 视图:ERB 模板,生成 HTML
│ ├── helpers/ # 视图辅助方法
│ ├── jobs/ # 后台任务(Active Job)
│ └── mailers/ # 邮件
├── config/
│ ├── routes.rb # 路由表:URL → 控制器动作
│ ├── database.yml # 数据库连接配置
│ └── environments/ # 开发/测试/生产三套环境配置
├── db/
│ ├── migrate/ # 数据库迁移文件(改表结构全靠它)
│ └── schema.rb # 当前表结构的快照(自动生成,别手改)
├── Gemfile # 依赖清单(≈ Maven 的 pom.xml / npm 的 package.json)
├── bin/ # rails、rake 等命令的入口脚本
└── public/ # 静态文件,直接对外暴露
步骤 3:用生成器造一个完整功能
Rails 最震撼初学者的是 scaffold 生成器,一条命令产出全套 MVC:
bin/rails generate scaffold Article title:string body:text
bin/rails db:migrate
第一条命令生成了:迁移文件、Article 模型、ArticlesController(含 7 个 REST 动作)、8 个视图模板、路由、测试文件。第二条命令执行迁移,创建 articles 表。
重启服务器,访问 http://localhost:3000/articles,你已经拥有一个能增删改查的文章管理系统——没写一行业务代码。
步骤 4:在 rails console 里玩一下数据
# 创建一条记录(立即落库)
article = Article.create(title: "你好 Rails", body: "这是正文")
# 查询
Article.count # => 1
Article.first.title # => "你好 Rails"
# 更新
article.update(title: "你好 Rails 7")
# 删除
article.destroy
rails console 是一个加载了完整应用环境的 IRB(Ruby 交互终端),调试数据问题时的第一利器,相当于 Spring Boot 里随手开了一个能调所有 Bean 的 REPL。
步骤 5:理解 Gemfile
# Gemfile
source "https://rubygems.org"
gem "rails", "~> 7.1.0" # 框架本体
gem "sqlite3", "~> 1.4" # 数据库驱动
gem "puma", ">= 5.0" # Web 服务器
group :development, :test do
gem "debug", platforms: %i[ mri windows ]
end
~> 7.1.0 表示「>= 7.1.0 且 < 7.2」,锁定次版本号。改完 Gemfile 执行 bundle install 安装依赖,依赖会被记录在 Gemfile.lock(≈ package-lock.json),保证团队和服务器环境一致。
讲清楚要点
- MVC 是职责分工不是分层摆设:Model 管数据和业务规则,Controller 管请求调度和流程控制,View 只管展示。控制器里写 SQL、视图里查数据库,都是新手最常见的越界。
- 约定优于配置:模型叫
Article(单数),表名自动是 articles(复数);控制器叫 ArticlesController,视图目录自动是 app/views/articles/。遵守约定就零配置,违反约定才需要写配置。
- 生成器是脚手架不是黑魔法:
rails generate 生成的每一行代码你都能改、能删。它只是把套路代码先写出来,省的是打字时间。
- 迁移是改表的唯一正规途径:永远不要手动连数据库
ALTER TABLE,要用 migration 文件,这样表结构变更才有版本历史、可回滚、可同步给团队。
- 全栈一体 vs 前后端分离:Rails 默认服务端渲染 HTML(配合 Hotwire 做交互),适合小团队快速出产品;Spring Boot + Next.js 分离架构适合大团队并行开发、多端复用 API。没有优劣,看场景。
你可能会懵的地方
-
Ruby 和 Rails 是什么关系?
Ruby 是编程语言,Rails 是用 Ruby 写的 Web 框架。关系就像 Java 和 Spring Boot。学 Rails 过程中自然会学会够用的 Ruby,不必先把 Ruby 学透再学 Rails。
-
bin/rails 和 rails 命令有什么区别?
bin/rails 用的是当前项目 Gemfile.lock 锁定的 Rails 版本(binstub),裸 rails 用的是系统全局安装的版本。项目里永远用 bin/rails,避免版本错配。
-
为什么服务器能自动加载我改的代码?
开发模式下 Rails 用 Zeitwerk 自动加载器,每个请求前检查文件变动并重新加载,所以改完代码不用重启(但改 Gemfile、config 初始化文件需要重启)。
-
SQLite 能用于生产吗?
能,Rails 7.1 之后官方越来越认可中小项目直接用 SQLite 上线。但团队协作和多机部署场景下,MySQL/PostgreSQL 仍是主流选择,本站用的就是 MySQL。
-
public 目录和 app/assets 都能放静态文件,区别在哪?
public/ 里的文件原样暴露(如 robots.txt),app/assets 和 app/javascript 里的会经过资产管道处理(压缩、指纹)。详见「资产与后台任务」一课。
错法对照
| 错法 | 问题 | 正解 |
|---|
手改 db/schema.rb | 它是自动生成的快照,手改会被覆盖 | 写 migration 文件,跑 db:migrate |
| 直接连数据库改表结构 | 团队无法同步,无回滚能力 | rails generate migration + db:migrate |
| 把业务逻辑堆在 Controller | 控制器臃肿难测,违反 MVC | 逻辑下沉到 Model 或 Service 对象 |
Gemfile 改完忘跑 bundle install | 启动报 Could not find gem | 改 Gemfile 后必跑 bundle install |
用全局 rails 命令操作项目 | 版本错配,行为诡异 | 项目内一律 bin/rails |
卡点急救
rails server 报端口被占用(Address already in use):换端口启动 bin/rails server -p 3001,或找到占用进程杀掉(Windows 用 netstat -ano | findstr :3000)。
bundle install 卡住或失败:国内网络换镜像,bundle config mirror.https://rubygems.org https://gems.ruby-china.com 后重试。
- 访问页面报
PendingMigrationError:有迁移文件没执行,跑 bin/rails db:migrate 即可。
面试常问
-
Rails 的「约定优于配置」具体体现在哪些地方?
答:命名约定(模型单数/表复数、控制器复数)、目录约定(视图按控制器名分目录)、主键默认 id、外键默认 xxx_id、时间戳默认 created_at/updated_at。遵守约定零配置,这是 Rails 开发效率高的核心原因。
-
MVC 三层各自的职责?一个请求如何流转?
答:Router 把 URL 分发到 Controller 的某个动作;Controller 调用 Model 读写数据,把结果存进实例变量;View 模板读取实例变量渲染 HTML 返回。Controller 是调度者,Model 承载业务,View 只做展示。
-
Rails 全栈方案和前后端分离(如 Spring Boot + Next.js)怎么选?
答:看团队规模、客户端数量和交互复杂度。小团队、单一 Web 端、内容型产品,Rails 全栈(服务端渲染 + Hotwire)交付速度最快;多端(App/小程序/Web)复用 API、前端交互极重、前后端团队独立协作时,前后端分离更合适。
自测三问
- 从浏览器发起
GET /articles/3 到看到页面,中间经过哪些组件,顺序是什么?
Gemfile 和 Gemfile.lock 的分工是什么?为什么两个都要提交到 Git?
- 为什么说「永远不要手改 schema.rb」?正确的做法是什么?
小结一句话
Rails 用「约定优于配置」把路由、控制器、模型、视图串成一条高效流水线,一条 scaffold 命令即可跑通全栈 CRUD。
延伸