什么是 MVVM
MVVM 是 Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式[1]。在这个模式中,三个组成部分各司其职:
- Model(模型):负责处理业务逻辑以及和服务器端进行交互,代表应用程序的数据和业务逻辑[1][4]
- View(视图):负责将数据模型转化为 UI 展示出来,可以简单理解为 HTML 页面[1][4]
- ViewModel(视图模型):用来连接 Model 和 View,是 Model 和 View 之间的通信桥梁[1][4]
Vue.js 与 MVVM 的关系
Vue.js 专注于 MVVM 模型的 ViewModel 层[6][8]。它通过双向数据绑定把 View 层和 Model 层连接了起来[6]。在 Vue.js 中,每个 Vue 实例都是一个 ViewModel[6][8]。
具体在 Vue 单文件组件中的体现:
- Template 部分:对应 View 层,负责 UI 展示[12]
- Data 属性:对应 Model 层,存储应用数据[12]
- Methods、Computed 等:对应 ViewModel 层,处理业务逻辑[12]
MVVM 的核心特性
双向数据绑定
MVVM 最重要的特性是双向数据绑定[9][10]。这意味着:
- Model 到 View:当数据发生变化时,视图会自动更新[9]
- View 到 Model:当用户在视图上进行操作时,数据也会自动同步更新[9]
在 Vue 中,v-model 指令完美体现了这一特性[9]。它通过监听 DOM 事件和更新数据属性,在 View 和 Model 之间建立了双向数据绑定[9]。
数据劫持机制
Vue 通过数据劫持来实现响应式[11][14]。具体原理是:
- 使用
Object.defineProperty劫持对象属性的 setter 和 getter 操作[11][14] - 当访问或修改属性时,Vue 会触发相应的拦截器[14]
- 通过这种方式监听数据变化,从而实现自动更新视图[11]
MVVM 架构的工作原理
在 MVVM 架构下,View 层和 Model 层并没有直接联系,而是通过 ViewModel 层进行交互[1][7]。具体流程包括:
-
初始化阶段:
- 实现监听器 Observer,对数据对象的所有属性进行监听[20]
- 实现指令解析器 Compile,解析模板指令并绑定更新函数[20]
- 实现订阅者 Watcher,连接 Observer 和 Compile[20]
-
数据变化时:
- 数据变化触发 setter[22]
- 订阅器 Dep 发布通知[22]
- 订阅者 Watcher 接收通知并执行更新函数[22]
- 视图自动更新[22]
MVVM 的优缺点
优点
- 分离关注点:将模型、视图和视图模型分离,使代码结构更清晰,便于维护和扩展[17][18]
- 提高重用性:一个 ViewModel 可以绑定不同的 View,视图逻辑可以重用[17][18]
- 自动更新 DOM:利用双向绑定,数据更新后视图自动更新,开发者无需手动操作 DOM[17][18]
- 提高可测试性:ViewModel 的存在可以帮助开发者更好地编写测试代码[17][18]
缺点
- 调试困难:使用双向绑定模式时,界面异常可能来自 View 或 Model,定位问题变得困难[17][18]
- 内存开销:大模块中 Model 也会很大,长期持有会造成更多内存消耗[17][18]
- 学习成本:需要学习新的概念和技术,有一定的学习曲线[17]
- 复杂性增加:对于大型图形应用程序,ViewModel 的构建和维护成本较高[17][18]
MVVM 与其他架构模式的区别
与 MVC 的区别
MVVM 与 MVC 最大的区别在于实现了 View 和 Model 的自动同步[19]。在 MVC 中,当 Model 的属性改变时,需要手动操作 DOM 元素来改变 View 的显示[19]。而在 MVVM 中,这个过程是自动的[19]。
与 MVP 的区别
MVVM 模式将 MVP 中的 Presenter 改名为 ViewModel,基本上与 MVP 模式一致[21]。唯一的区别是,MVVM 采用双向绑定:View 的变动自动反映在 ViewModel,反之亦然[21]。
总结
Vue.js 的 MVVM 架构通过双向数据绑定和数据劫持机制,实现了数据驱动视图的开发模式[7][15]。这种架构使得开发者只需关注业务逻辑,无需手动操作 DOM,同时提供了良好的代码组织结构和可维护性[7]。虽然存在一些缺点,但 MVVM 仍然是现代前端开发中非常重要和实用的架构模式。
[1] https://juejin.cn/post/6844903929298288647 [2] https://blog.csdn.net/a1598452168YY/article/details/128516306 [3] https://www.kancloud.cn/dataoedu/vue/327305 [4] https://blog.csdn.net/m0_59704905/article/details/138116193 [5] https://stackoverflow.com/questions/53723179/is-the-vue-instance-component-only-the-viewmodel-in-the-mvvm [6] https://012-cn.vuejs.org/guide/ [7] https://www.cnblogs.com/angel648/p/15901113.html [8] https://012.vuejs.org/guide/ [9] https://www.cnblogs.com/XiaYu-Ye/p/18068324 [10] https://vue3js.cn/interview/vue/bind.html [11] https://segmentfault.com/a/1190000043082630 [12] https://lzwdot.com/docs/26955 [13] https://iming.work/detail/5b0292d017d009726f2adf6e.html [14] https://www.nowcoder.com/discuss/520668659241484288 [15] https://blog.csdn.net/weixin_52148548/article/details/125661886 [16] https://juejin.cn/post/7307224593457479720 [17] https://blog.csdn.net/qq_37255976/article/details/136400081 [18] https://juejin.cn/post/7185543497485811770 [19] https://www.cnblogs.com/ranyonsue/p/12090647.html [20] https://juejin.cn/post/6844904183938678798 [21] https://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html [22] https://blog.csdn.net/m0_71981318/article/details/125403283 [23] https://blog.csdn.net/m0_67849954/article/details/127119146 [24] https://github.com/WindrunnerMax/EveryDay/blob/master/Vue/MVVM%E6%A8%A1%E5%BC%8F%E7%9A%84%E7%90%86%E8%A7%A3.md [25] https://blog.csdn.net/sdgfafg_25/article/details/136681403 [26] https://juejin.cn/post/7075569849178521608 [27] https://yingchenit.github.io/vue/watcher/ [28] https://blog.csdn.net/zhaojjjjjj163/article/details/135494562 [29] https://cloud.tencent.com/developer/news/54080 [30] https://www.zhihu.com/question/27791075