MVC、MVP、MVVM概念、区别与各自特点
MVC:
介绍:
MVC 模型即 Model View Controller(模型 视图 控制器)
Model(模型):存储业务数据、验证、业务逻辑(不包含回调函数),
View(视图):数据展示层,负责数据展示与交互,不能直接操作数据。
Controller(控制器):负责接收 View 用户的操作,然后更改数据,同时更改 Model 层数据,是 M 和 V 的中间层。
工作流程:
View <—> Controller <—> Model,单向通信为主,Controller是核心调度者。
优缺点:
优点:使用好时耦合性较低,部署快,可维护性高。
缺点:使用不规范时耦合性极高,特别是 view 层和 Controller 层,其次是 View 层和 Model 层。
下面一个原生 js/HTML 实现 MVC 示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>高耦合 MVC(经典版)</title>
</head>
<body>
<input type="text" id="input">
<button id="addBtn">添加</button>
<div id="list"></div>
<script>
// ======================
// 1. Model(数据层)
// ======================
const Model = {
items: [],
addItem(text) {
this.items.push(text);
}
};
// ======================
// 2. View(直接依赖 Model!!!耦合)
// ======================
const View = {
render() {
// View 直接读取 Model 数据!!!
// View ←→ Model 直接耦合!
document.getElementById('list').innerHTML
= Model.items.map(i => `<div>${i}</div>`).join('');
}
};
// ======================
// 3. Controller
// ======================
const Controller = {
init() {
// 直接操作 DOM 元素!View 和 Controller 耦合!
document.getElementById('addBtn').onclick = () => {
const text = document.getElementById('input').value.trim();
if (text) {
Model.addItem(text);
View.render(); // 调用View
}
};
}
};
Controller.init();
</script>
</body>
</html>
可以很明显的看到缺点: Controller 直接操作 View 的内部元素 / 方法 ,即 controller 完全知道 view 里面有什么,要什么,这使得一个 view 的 controller 在其它 view 里面基本无法使用,view 和 controller 联系过于紧密,同时view 层对 model 层的访问效率极低。
View 层和 Model 层直接通信——如上图,View 初始渲染时直接使用在 Model 层查询状态,Model 数据更新后直接
MVP:
介绍:
MVP 模型即 Model View Presenter(模型 视图 主持人)。
Model(模型):同 MVC 一样,都是数据存放地,但不和 View 直接耦合,下面会讲。
View(视图):同 MVC 一样,都是数据展示地,也不和 Model 直接耦合。
Presenter(主持人):是 MVP 的核心,负责连接 View 和 Model,直接解决 View 和 Model 耦合性问题,它接收 View 的用户操作,调用 Model 的方法处理数据,然后将处理好的数据推给 View,使视图更新。
工作流程:
这里就可以看到,Presenter 相较于 Controller 对于 View 和 Model 的约束作用更大,解决 MVC 的第二个耦合问题。
优缺点:
优点:降低了程序的耦合性,提高灵活性,即 视图与住处人 视图与模型 的耦合度降低了
缺点:View 和 Presenter 的耦合性虽然低了,但写起来并不简单,需要 Presenter 把 view 中的所有方法都写下来,同时 view 也要暴漏所有的方法,更难受的是当数据变了要手动通知视图,视图变了要手动更改数据,写起来很繁琐。
如下面代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>真正的 MVP(低耦合)</title>
</head>
<body>
<input type="text" id="input">
<button id="addBtn">添加</button>
<div id="list"></div>
<script>
// ======================
// 1. Model:完全独立
// ======================
const Model = {
items: [],
addItem(text) {
this.items.push(text);
return this.items;
}
};
// ======================
// 2. View:只暴露接口,不暴露内部元素
// ======================
const View = {
// 内部元素全部私有化!
$input: document.getElementById('input'),
$list: document.getElementById('list'),
$btn: document.getElementById('addBtn'),
// 暴露给 Presenter 的【接口】
getValue() {
return this.$input.value.trim();
},
renderList(items) {
this.$list.innerHTML = items.map(i => `<div>${i}</div>`).join('');
},
clearInput() {
this.$input.value = '';
},
// 事件绑定由 View 自己做!只抛回调!
bindAddClick(callback) {
this.$btn.onclick = callback;
}
};
// ======================
// 3. Presenter:完全不碰 View 内部!只调接口
// ======================
const Presenter = {
init() {
// View 只给回调,Presenter 不知道按钮是什么
View.bindAddClick(() => this.handleAdd());
},
handleAdd() {
const text = View.getValue(); // 只调用接口,注意基本上所有view暴露的方法都要调用
if (!text) return;
const newList = Model.addItem(text);
View.renderList(newList);
View.clearInput();
}
};
// 启动
Presenter.init();
</script>
</body>
</html>
MVVM:
介绍:
MVVM 模型即 Model View ViewModel (模型 视图 视图模型)
Model(模型):数据存放地,同上
View(视图):数据展示地,同上,可以通过指令(如 vue 中的v-model、v-bind)直接绑定ViewModel的数据,无需手动接收数据更新。
ViewModel(视图模型):它负责将Model的数据转换为View可展示的格式(数据格式化),同时监听View的用户操作,反向修改Model的数据(双向绑定)。ViewModel通过「数据双向绑定」让View和Model自动同步,无需手动调度——当Model的数据变化时,View会自动更新;当用户操作View时,Model的数据也会自动修改。
工作流程:
view 和 model 需要做的事情变化不大,但 viewmodel 相较于 presenter 变化确实极大
如下:
-
- 持有 Model(数据来源)
- 提供视图可绑定的数据
- 监听 Model 变化 → 自动更新 View
- 接收 View 操作 → 自动更新 Model
即它不再主动调用 view 暴露的方法了,而是通过数据双向绑定的方法来实现数据分配于更改,像 vue 里面通过 Object.defineProperty 方法实现数据劫持,vue3 里面使用 Proxy,都是 ViewModel 的表现形式。
优缺点:
优点:解决了 MVC 和 MVP 都没有解决或完全解决的 Model 层和中间调节层(Controller/Presenter)的高度耦合性。提高了代码的重用性,写起来方便。
缺点:类会增多,ViewModel会越加庞大,调用的复杂度增加。(但问题不大,相较于前两个算很好了,推荐推荐推荐)
代码示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原生 JS 实现 MVVM(双向绑定)</title>
</head>
<body>
<h3>MVVM 示例</h3>
<input type="text" id="input">
<div id="list"></div>
<script>
// ======================
// 1. Model(数据层)
// ======================
const Model = {
items: [],
inputValue: ""
};
// ======================
// 2. ViewModel(VM 核心:双向绑定)
// ======================
const ViewModel = {
model: Model,
viewInput: document.getElementById("input"),
viewList: document.getElementById("list"),
// 初始化:监听视图 + 监听数据
init() {
// 视图 → 数据
this.viewInput.oninput = (e) => {
this.model.inputValue = e.target.value;
};
// 数据 → 视图(自动渲染)
this.observe(this.model, "inputValue", () => {
this.viewInput.value = this.model.inputValue;
});
// 列表自动渲染
this.observe(this.model, "items", () => {
this.renderList();
});
},
// 添加数据
addItem() {
if (this.model.inputValue) {
this.model.items.push(this.model.inputValue);
this.model.inputValue = "";
}
},
// 渲染列表
renderList() {
this.viewList.innerHTML = this.model.items
.map(i => `<div>${i}</div>`)
.join("");
},
// 数据监听:数据变 → 视图自动变(MVVM 核心)
observe(obj, key, callback) {
let value = obj[key];
Object.defineProperty(obj, key, {
set(newVal) {
value = newVal;
callback(); // 自动更新视图
},
get() {
return value;
}
});
}
};
// ======================
// 3. View(视图层:无逻辑!)
// ======================
// View 只做展示,不处理逻辑,不调用 VM
document.getElementById("input").onkeydown = (e) => {
if (e.key === "Enter") ViewModel.addItem();
};
// 启动
ViewModel.init();
</script>
</body>
</html>
上面的示例都是使用原生 js/HTML 进行的简单实现,可能有误,理解即可。
参考博客如下:
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)