基本原理
脏刷新基本遵循订阅者设计模式,$watch添加监听者到$$watchers,当执行$degist的时候,读取watchers列表,当发现数值变动的时候,对页面进行刷新,与getter/setter最大的不同有两个地方,1.当数值发生变动的时候,需要主动执行$degist进行主动刷新,2.$degist会扫描监听列表,如果监听列表过长,容易导致性能问题。
代码实现
1.定义$watch函数,记录要监听的属性,并将该属性放到$$watcher监听列表里面。
$watch: function (key, listenerFn) {
var self = this;
var watchFn = function (scope) {
return {value: scope.model[key], key: key};
}
var watcher = {
watchFn: watchFn,
listenerFn: listenerFn || function () {}
};
this.$$watcher.push(watcher);
}
2.$digest主动数据刷新,函数会读取监听列表里面的元素,新旧数值进行对比,如果发现数据不一样,将会重新渲染。
$digest: function () {
var self = this;
var length = this.$$watcher.length;
var watcher, newValue, oldvalue, key;
while(length--){
watcher = this.$$watcher[length];
console.log(watcher)
newValue = watcher.watchFn(this).value;
key = watcher.watchFn(this).key;
oldValue = watcher.last;
if(newValue !== oldValue){
watcher.last = newValue;
var arr = self.model2sync[key]
each(arr, function() {
// 更新节点的text
this.node.textContent = self.renderStr(this.raw)
})
// var arr = self.model2sync[key]
// console.log(arr)
watcher.listenerFn(newValue, oldValue, this, key);
}
}
}
3.改动model数据,主动执行$digest函数,进行脏值检查。
var demo1 = mvvm('#demo1', {
model: {
name: 'world',
time: Date(),
css: 'green'
}
});
demo1.$watch('time')
setInterval(function () {
demo1.model.time = Date();
demo1.$digest();
}, 1000);
4.完整代码
http://codepen.io/youyudehexie/pen/NxbqPz
对比Getter/Setter的优劣
优点
先来一个重要的例子,列表页刷新。
// 此处是伪代码,renderData 与某个双向绑定模板绑定。
var renderData = [0...1000];
renderData[] = 0; //默认所有数组元素为0.
for (var i = 0; i < 1000; i++) {
renderData[i] = 1;
}
当我们使用Getter/Setter的时候,实际上,每次修改数值都会激活刷新模版的方法,而脏刷新则可以在完成1000个元素的数值变动后,统一刷新到Dom,只需要刷新1次,显然而见,脏刷新优势巨大。
缺点
- 当监听元素变多的时候,watcher列表会变得很长,每次都要花费大量的计算,才能找到变动的数据块。
- 需要主动调用$degist才会刷新,Getter/Setter 方法显得更加智能。