重点关注三个类
- Watcher:(vm)
一个vm(vierModel)实例对应一个Watcher实例,用来接收通知,对比新旧Vnode,更新页面。
tips
watcher.value = vm
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
...
};
Watcher.prototype.addDep = function addDep (dep) {
...
dep.addSub(this);
...
};
Watcher.prototype.update = function update () {
...
this.run();
...
};
Watcher.prototype.run = function run () {
...
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
}
...
};
- Observer: (vm.data)
更像是一个函数合集,用来将vm.data转成响应式,同时对data的每一个属性生成一个Dep类实例
tips
vm.data.__ob__ = observer
observer.value = vm.data
var Observer = function Observer (value) {
...
this.walk(value);
...
}
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
...
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
var getter = property && property.get;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); //这里绕来绕去,直接dep.addSub(Dep.target)不香吗
}
return value
},
set: function reactiveSetter (newVal) {
...
dep.notify();
}
});
...
}
- Dep: ()
每一个响应式属性都有一个Dep实例相对应,dep.subs存放着使用了该属性的所有Watcher实例
var Dep = function Dep () {
this.id = uid++;
this.subs = [];
};
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
};
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
Dep.prototype.notify = function notify () {
// 调用Watcher.prototype.update()对比节点,更新页面
var subs = this.subs.slice();
if (!config.async) {
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
以下为流程
- 假设现在有第一个vm1,调用了new Watcher(vm1),将Dep.target指向该Watcher;只后又调用了new Observer(vm1.data)
结果是vm1.data的每一个属性都有一个Dep实例与之对应 - 现在有第二个vm2,调用了new Watcher(vm2),将Dep.target指向该Watcher;只后又调用了new Observer(vm2.data),在这个时候vm2.data里面使用到了vm1.data的某属性,那么通过该属性的存取描述符get,会将Dep.target(也就是第二个Watcher实例:上面new Watcher(vm2)产生的值)存入dep.subs数组里面。
- vm1.data的某属性修改了,触发了该属性的存取描述符set,调用了该属性对应的dep.notify(),结果遍历了dep.subs里面的每一个Watcher实例的update方法。
- Watcher.prototype.updata()根据传入的参数(是否缓存如队列,整体更新)来对比Vnode,渲染页面