MVVM是什么
MVVM是Model-View-ViewModel的简写。它模式是MVC—>MVP—>MVVM的进化版。
Model负责用JavaScript对象表示,View负责UI界面显示,两者做到了最大限度的分离。
而把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的界面修改同步回Model更新数据。
主流MVVM框架和实现做法
- 脏值检查(angular.js)
- 发布者-订阅者模式+数据劫持(vue.js)
脏值检查: angular.js 是通过脏值检测的方式来比对数据是否有变更而决定是否更新视图。
原理是,拷贝一份copy_viewModel
在内存中,用户操作导致viewModel
发生改变的行为时,框架都会把copy_viewModel
和最新的viewModel
进行深度比较,一旦发现有属性发生变化,则重新渲染与之绑定的DOM节点。
最简单的方式就是通过setInterval()
定时轮询检测数据变动,angular触发时进入脏值检测。但只限 指定的事件 (如:用户点击,输入操作,ajax请求,setInterval,setTimeout等...),否则需手动调用apply
函数去强制执行一次脏检查。
数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
在数据变动时发布消息给订阅者,触发相应的监听回调,而产生更新数据和视图。
vue数据双向绑定原理
原理图告诉我们,data属性定义了getter、setter对属性进行劫持,当属性值改变是就会notify通知watch对象,而watch对象则会重新触发组件呈现功能,继而更新view上的DOM节点树。
反之,view上输入数据时,也会触发data变更,也会触发订阅者watch更新,这样子model数据就可以实时更新view上的数据变化。这样一个过程就是vue的数据双向绑定了。
vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()
来实现对属性的劫持,达到监听数据变动的目的。
Object.defineProperty
Object.defineProperty
是ES5一个方法,可以直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象,对象里目前存在的属性描述符有两种主要形式:数据描述符和 存取描述符。
数据描述符是一个拥有可写或不可写值的属性。
存取描述符是由一对getter-setter函数功能来描述的属性。
描述符必须是两种形式之一;不能同时是两者。即:有值和可写,或者可get和set
属性描述符包括:
- Configurable(可配置性相当于属性的总开关,只有为true时才能设置,而且不可逆)、
- Enumerable(是否可枚举,为false时for..in以及Object.keys()将不能枚举出该属性)、
- Writable(是否可写,为false时将不能够修改属性的值)、
- Value(属性的值,默认为undefined)、
- Get(一个给属性提供getter的方法)、
- Set(一个给属性提供setter的方法)、
var Book = {}
Object.defineProperty(Book, 'name', {
get: function () {
return '《' + name + '》'
},
set: function (value) {
name = value;
console.log('你取了一个书名叫做' + value);
}
})
console.log(Book.name); // 《》
Book.name = 'vue权威指南'; // 你取了一个书名叫做vue权威指南
console.log(Book.name); // 《vue权威指南》
实现过程
我们已经知道怎么实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer
,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher
看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep
来专门收集这些订阅者,然后在监听器Observer
和订阅者Watcher
之间进行统一管理的。接着,我们还需要有一个指令解析器Compile
,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher
,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher
接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。
因此接下去我们执行以下4个步骤,实现数据的双向绑定:
- 实现一个监听器
Observer
,用来劫持并监听所有属性,如果有变动的,就拿到最新值并通知订阅者。 - 实现一个订阅者
Watcher
,连接Observer
和Compile
。可以订阅并收到每个属性的变化通知并执行指令绑定的相应函数,从而更新视图。 - 实现一个解析器
Compile
,可以扫描和解析每个节点的相关指令,并根据初始化模板替换数据,以及绑定相应的更新函数。 - mvvm入口函数,整合以上三者。
Document
{{title}}
{{name}}
aaaa{{xxx}}zzzz
参考链接:
深入响应式原理
剖析Vue原理&实现双向绑定MVVM
《响应式系统的基本原理》.js
JavaScript实现MVVM之我就是想监测一个普通对象的变化