通过简单类比和真实示例理解模型-视图-控制器图。了解 MVC 如何组织代码以实现可扩展、可维护的软件。
January 23, 2026 (2mo ago)
模型-视图-控制器 图解实用指南
通过简单类比和真实示例理解模型-视图-控制器图。了解 MVC 如何组织代码以实现可扩展、可维护的软件。
← Back to blog
A Model View Controller 图不仅仅是技术图纸;它是应用架构的蓝图。它展示了如何将复杂系统拆分为三个不同但相互关联的部分:Model(模型,数据和核心逻辑)、View(视图,用户所见)和 Controller(控制器,协调者)。这种分离使代码更可扩展、更易测试且更易维护。1
分解 MVC 架构模式
为了让该模式更具象,想象一家餐厅。
- Model 是厨房:它保存原料(数据)和配方(业务规则)。
- View 是菜单和餐桌布置:它向客人展示选项和成品菜肴。
- Controller 是服务员:它从 View 接收订单,要求 Model 准备,并将结果返回给 View。
这种“关注点分离”是 MVC 的核心优势,有助于团队避免紧耦合的代码,这类代码难以更改或测试。2
在实践中,MVC 图充当一种共享语言,让开发者、产品经理和利益相关者就应用如何工作达成一致。清晰的图示能加快入职速度并减少团队间的误解。3
分解核心 MVC 组件
Model:操作的大脑
Model 管理数据、状态和业务规则。它是单一真相来源,不应了解诸如 HTML 或 CSS 之类的呈现细节。它的职责包括验证、持久化,以及向系统其他部分暴露所需的操作。
View:应用的面孔
View 的唯一工作是呈现。它从 Model(通常通过 Controller)接收数据,渲染 UI,并捕获用户交互。View 不应直接更改应用数据;它只应将用户操作通知 Controller。
Controller:交通指挥
Controller 解释用户输入、协调 Model 更新,并选择 View 应如何响应。保持控制器精简:应将繁重工作委托给模型或服务类,避免嵌入复杂的业务逻辑。
角色与职责(快速参考)
| Component | Primary Responsibility | Common Pitfalls to Avoid |
|---|---|---|
| Model | Manage data, enforce business rules | Mixing in UI logic or rendering HTML |
| View | Render data and capture input | Mutating data or holding business logic |
| Controller | Coordinate input and model updates | Performing heavy data processing or DB queries directly |
MVC 如何处理用户请求——逐步说明
以典型的联系表单提交为例,观察 MVC 的运行:
- 用户与 View 交互(填写表单并点击“提交”)。
- View 将收集的输入通知 Controller。
- Controller 验证并将处理委托给 Model。
- Model 验证、保存数据并更新状态。
- View 重新渲染以显示新状态(例如,“感谢您的留言!”确认)。
这种单向流程减少了耦合,使得对系统的推理变得直观。阻止 View 与 Model 直接通信可以避免隐藏的依赖关系和“意大利面条式代码”。2
在现代代码中实践 MVC
典型的 Node.js + Express 后端配合 React 前端能很好地映射到 MVC。一个简单的文件夹结构是:
/project-root ├── /src │ ├── /controllers # 处理传入请求并协调响应 │ ├── /models # 管理数据和业务规则 │ └── /views_or_components # React 组件或服务器端视图
Example controller (TypeScript + Express):
// src/controllers/userController.ts
import { Request, Response } from 'express';
import { User } from '../models/userModel';
export const getUserProfile = (req: Request, res: Response) => {
const userId = req.params.id;
const user = User.findById(userId);
if (user) {
res.status(200).json(user);
} else {
res.status(404).send('User not found');
}
};
Example model (conceptual):
// src/models/userModel.ts
const users = [
{ id: '1', name: 'Alex Doe', email: 'alex@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' },
];
export class User {
static findById(id: string) {
return users.find(user => user.id === id);
}
}
React component (View):
// src/components/UserProfile.tsx
import React, { useState, useEffect } from 'react';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
if (!user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
};
这种结构使每一层都更专注且易于测试,并帮助团队在不产生缠结依赖的情况下扩展代码库。有关 MVC 基础的更多内容,请参见 Codecademy 的概览。1
将 MVC 与其他设计模式比较
MVC 是经典模型,但根据 UI 复杂性和测试目标,MVP 或 MVVM 有时更合适。
- MVP(Model–View–Presenter):Presenter 承担呈现逻辑并驱动被动的 View。在你想最大化 UI 可测试性时很有用。
- MVVM(Model–View–ViewModel):ViewModel 暴露可绑定的数据和命令;View 将其绑定。由于数据绑定和响应式更新,该模式在现代 UI 框架中很受欢迎。5
每种模式都在不同的权衡中进行优化:分离与清晰(MVC)、可测试性(MVP)或 UI 响应性与减少样板代码(MVVM)。
常见的 MVC 错误及修复方法
即使有正确的 MVC 图示,团队也可能陷入会积累技术债务的反模式。
肥大控制器
当控制器包含业务逻辑、计算或直接的数据库工作时,它们会变得难以测试和重用。将复杂逻辑移入模型或专用服务类,让控制器作为协调者。
贫血模型
当模型仅包含数据而无行为时,业务规则会散落在控制器和服务中。将行为重新引入模型,使其负责自身的不变量和操作。Martin Fowler 对贫血领域模型的讨论很有帮助。4
避免让 View 与 Model 直接通信;所有交互应通过 Controller 流动,以保持清晰的关注点分离。2
常见问题解答 — 常见的 MVC 问题
在像 React 这样的框架下 MVC 仍然有用吗?
有用。React 覆盖了视图层,但你仍然需要一个放置应用状态和业务规则的地方(Model),以及将状态更改连接到 UI 的方式(Controller 或等价物)。将这些角色分开可以防止 React 组件变得臃肿。
团队在采用 MVC 时最常犯的错误是什么?
最常见的错误是创建肥大控制器。通过将验证和业务逻辑委托给模型或服务来保持控制器精简。
MVC 图如何帮助团队协作?
清晰的图示是共享蓝图。它减少了歧义,加速了入职,并让团队可以并行工作而不互相越权。
在 Clean Code Guy,我们帮助团队将这些原则应用于构建经久耐用的软件。访问我们的指南和服务: https://cleancodeguy.com。
AI编写代码。您让它持久。
在AI加速的时代,干净代码不仅仅是好的实践 — 它是能够扩展的系统与在自己的重量下崩溃的代码库之间的区别。