MVVM和MVC的理解
MVVM的定义
MVVM
是Model-View-ViewModel
的简写。即模型-视图-视图模型。
模型指的是后端传递的数据。视图指的是所看到的页面。视图模型mvvm
模式的核心,它是连接view
和model
的桥梁。
它有两个方向:一是将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。
总结:在
MVVM
的框架下视图和模型是不能直接通信的。它们通过ViewModel
来通信,ViewModel
通常要实现一个observer
观察者,当数据发生变化,ViewModel
能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel
也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM
中的View
和ViewModel
可以互相通信。MVVM流程图如下:
MVC的定义
MVC
是Model-View- Controller
的简写。即模型-视图-控制器。
M
和V
指的意思和MVVM
中的M
和V
意思一样。C
即Controller
指的是页面业务逻辑。
使用MVC
的目的就是将M
和V
的代码分离。MVC
是单向通信。也就是View
跟Model
,必须通过Controller
来承上启下。MVC
和MVVM
的区别并不是VM
完全取代了C
,ViewModel
存在目的在于抽离Controller
中展示的业务逻辑,而不是替代Controller
,其它视图操作业务等还是应该放在Controller
中实现。也就是说MVVM
实现的是业务逻辑组件的重用。由于mvc
出现的时间比较早,前端并不那么成熟,很多业务逻辑也是在后端实现,所以前端并没有真正意义上的MVC
模式。而我们今天再次提起MVC
,是因为大前端的来到,出现了MVVM
模式的框架,我们需要了解一下MVVM
这种设计模式是如何一步步演变过来的。
为什么会出现 MVVM 呢?
MVC
架构模式对于简单的应用来看是OK 的,也符合软件架构的分层思想。 但实际上,随着H5 的不断发展,人们更希望使用H5 开发的应用能和Native 媲美,或者接近于原生App 的体验效果,于是前端应用的复杂程度已不同往日,今非昔比。这时前端开发就暴露出了三个痛点问题:
- 开发者在代码中大量调用相同的 DOM API,处理繁琐 ,操作冗余,使得代码难以维护。
- 大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
- 当 Model 频繁发生变化,开发者需要主动更新到View ;当用户的操作导致 Model 发生变化,开发者同样需要将变化的数据同步到Model中,这样的工作不仅繁琐,而且很难维护复杂多变的数据状态。
其实,早期jquery 的出现就是为了前端能更简洁的操作DOM 而设计的,但它只解决了第一个问题,另外两个问题始终伴随着前端一直存在。MVVM 的出现,完美解决了以上三个问题。
MVVM
由Model、View、ViewModel
三部分构成,Model
层代表数据模型,也可以在Model
中定义数据修改和操作的业务逻辑;View
代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel
是一个同步View
和 Model
的对象。
在MVVM
架构下,View
和 Model
之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel
通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
MVVM框架下的Vue.js
Vue.js
可以说是MVVM
架构的最佳实践,专注于 MVVM
中的 ViewModel
,不仅做到了数据双向绑定,而且也是一款相对来比较轻量级的JS 库,API 简洁,很容易上手。 下面简单了解一下 Vue.js 关于双向绑定的一些实现细节:
Vue.js
是采用Object.defineProperty
的getter
和setter
,并结合观察者模式来实现数据绑定的。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用
Object.defineProperty
将它们转为getter/setter
。用户看不到getter/setter
,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
Observer
:数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty
的getter
和setter
来实现
Compile
:指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
Watcher
:订阅者,作为连接Observer
和Compile
的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数
Dep
:消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify
函数,再调用订阅者的update
方法
从图中可以看出,当执行 new Vue()
时,Vue 就进入了初始化阶段,
一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty
将它们转为 getter/setter
,实现数据变化监听功能;
另一方面,Vue 的指令编译器Compile 对元素节点的指令进行扫描和解析,初始化视图,并订阅 Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。
当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify()
,Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。