在实例化Vue的时候,会实例化出Observer,Observer的作用就是给数据域的所有数据进行重新定义。
observer的代码结构:
构造方法
ObserveAllPropertyInData
该方法会遍历整个数据域,对数据域中的每个属性进行重新定义。
defineReactive:
可以看到,定义里面多了Dep等对象,这个我们放在Watcher,也就是数据劫持那一章说
/**
* @param _MyData data域(被代理的真实数据域)
* @constructor
*/
function Observer(_MyData) {
this.ObserveAllPropertyInData(_MyData);
}
Observer.prototype = {
/**
* 不断递归data,一直到value类型不为object
* 然后对每个属性进行defineProperty
* @param _MyData data域(被代理的真实数据域)
* @constructor
*/
ObserveAllPropertyInData:function(_MyData) {
let me = this;
if( !_MyData || typeof _MyData !== 'object'){
return ;
}
Object.keys(_MyData).allKeys.forEach(function (key) {
me.ObserveAllPropertyInData(_MyData[key]);
let val = _MyData[key];
me.defineReactive(_MyData, key, val);// 数据域(动态变化的引用) 当前的key 当前的val
})
},
/**
* @param OldObj 当前递归到的数据域
* @param key 对应的key
* @param OldVal key对应的val
*/
defineReactive:function (OldObj, key, OldVal) {
let val = OldVal;// 当前数据域的val 如果是对象,那么这个OldVal就是一个对象
let dep = new Dep();
// 把当前数据域对应的Dep放入全局Dep数组中
Dep.testCntent.push(dep);
// 对当前数据域进行重新定义
Object.defineProperty(OldObj,key,{
configurable:false,
enumerable:true,
set:function (NewVal) {
if(val === NewVal)
return val;
// 这里采取的方法是,先把val取出来,然后set的话不要使用OldObj[key]=xxx,避免死循环
val = NewVal;
new Observer(NewVal);
// 这里只会触发就是说,给NewVal里面的所有属性 重新定义 并且 加一个Dep
// 更新该key下面的所有watcher【watcher和dep的关系在compile阶段建立,如果那个阶段没建立成功,那么这里也不会更新】
// 更新原则:只更新建立了联系,并且属性名不变的watcher,对于那些后来才加上的属性,一概不理会
// 你可以利用这个更新原则的漏洞,先写好一个不存在的属性的表达式,相当于占个位,后面再补上去,也是可以的,
// 因为watcher已经关联上了
// 这里有一个需要注意的话,被覆盖掉的属性,虽然存在watcher关联,但是重新解析表达式,会得到一个undefined,
// 结果更新到页面就为空字符了
dep.notify();
},
get:function () {
/**
* 这里有两种情况调用
* 1.普通调用,target为null
* 2.关联dep和watcher调用,target不为null
*/
if(Dep.target){
Dep.target.addDep(dep);
}
return val;
}
})
}
}
Dep.testCntent = [];
Dep.target = null;
Dep.id = 0;
function Dep(){
this.DepId = Dep.id++;
this.subs = [];
}
// 重复管理
Dep.prototype = {
AddWarcher:function (watcher) {
this.subs.push(watcher);
},
// 唤醒所有watcher
notify:function () {
this.subs.forEach(function (Watcher) {
// 调用watcher,重新解析表达式,得到最新的值,更新到界面上
Watcher.Update();
})
}
}