你有没有遇到过这样的情况:在写前端代码时,页面上一个数字要随着用户操作实时变化,于是你一会儿查DOM、一会儿改innerHTML,逻辑一多就乱成一团?其实,这种问题早就有了更优雅的解法——MVVM。
MVVM 是什么?
MVVM 全称是 Model-View-ViewModel,它是一种用于构建用户界面的设计模式。名字听起来挺学术,但拆开来看其实很简单:
- Model:就是数据,比如用户的姓名、购物车里的商品列表。
- View:就是我们看到的网页界面,HTML + CSS 构成的结构和样式。
- ViewModel:它是中间的“翻译官”,把数据的变化自动反映到界面上,也把用户的操作反馈回数据。
它怎么工作的?一张图就能说清
想象一下你在用一个记账App,当你点击“收入+100”按钮,页面上的总额立刻变多,根本不用手动去改那个显示数字的标签。这就是 MVVM 在背后干活。
流程大概是这样:
- 你点击按钮 → ViewModel 接收到事件
- ViewModel 修改 Model 中的 total 值
- 由于数据绑定机制,View 自动更新显示
不需要你写 document.getElementById 再 innerText 赋值,一切自动完成。
数据绑定是关键
MVVM 的核心在于“数据绑定”。最常见的是一次性绑定和双向绑定。
比如你有这样一个简单的 ViewModel:
const viewModel = {
name: '小明',
age: 24
};
而你的 HTML 长这样:
<div>
姓名:<span data-bind="text: name"></span>,
年龄:<span data-bind="text: age"></span>
</div>
当 viewModel.name 变成“小红”时,页面上对应的 span 内容也会跟着变。这是怎么做到的?靠的是监听机制。
监听数据变化:Object.defineProperty 或 Proxy
在 Vue 2 里,用的是 Object.defineProperty 来劫持对象属性的 get 和 set。
let value = '默认值';
Object.defineProperty(viewModel, 'name', {
get() {
console.log('有人读取了 name');
return value;
},
set(newValue) {
console.log('name 被改为', newValue);
value = newValue;
// 触发视图更新
updateView();
}
});
每次修改 name,set 函数就会被触发,这时候就可以通知页面去刷新对应的部分。
到了 Vue 3,换成了更强大的 Proxy,可以监听整个对象,连新增或删除属性都能捕获。
const proxyVM = new Proxy(viewModel, {
set(target, key, value) {
target[key] = value;
updateView(); // 更新视图
return true;
}
});
ViewModel 怎么连接 View?
ViewModel 不直接操作 DOM,而是通过指令或模板语法告诉系统:“这个字段要绑定到哪个位置”。
比如 Vue 中的 {{ }} 插值:
<p>欢迎,{{ name }}!</p>
或者 Angular 中的 [(ngModel)] 实现双向绑定:
<input [(ngModel)]="name" />
输入框内容变了,data 里的 name 也跟着变;反过来,JavaScript 改了 name,输入框也立刻更新。
实际开发中的好处
以前做表单验证,可能得写一堆 if 判断 + getElementById + 提示文案显隐控制。用了 MVVM 后,数据一改,错误提示要不要显示自然就知道了。
比如:
const vm = {
username: '',
get showError() {
return this.username.length < 3 && this.username.length > 0;
}
};
模板里直接判断是否显示提示:
<div v-if="showError" class="error">用户名至少3位</div>
逻辑清晰,维护起来也省心。
别忘了“响应式”的代价
虽然自动更新很方便,但也不是没有成本。监听越多,内存占用越高,过度使用深层监听可能导致性能下降。所以大列表或高频更新场景下,该用 key 优化还得用,该手动控制更新也别偷懒。
MVVM 不是银弹,但它确实让大多数日常开发变得更轻松了。你现在用的 Vue、React(虽不是严格 MVVM,但思路相近)、Angular,底层都能看到它的影子。