详细文档可参考 https://github.com/DMQ/mvvm
总体思路
- 入口调用observer, 对于每一个property创建一个Dep, get方法负责将watcher添加到Dep中,set方法负责通知Dep中所有的watcher进行update操作
- 入口接着调用compile,遍历节点,找到所有含vue指令的节点,从modal中获取数据并渲染或者添加合适的监听事件,并创建watcher,wathcher的构造函数中会调用自身get,自身get函数首先将自身赋值给Dep.target,随后调用vue中property的get方法完成将watcher添加到Dep中的操作,另外watcher中的update方法会执行从compile传入的update的回调函数
主要分四个模块Observer, Dep, Compile, Watch
Observer: 进行数据劫持,并且通过Dep通知watchList进行更新
Object.defineProperty(data, key, {
var dep = new Dep();
get: function() {
Dep.target && dep.addDep(Dep.target); // 供watcher创建时调用
return val;
}
set: function(newVal) {
if (val === newVal) return
val = newVal;
dep.notify(); // 通知所有订阅者
}
});
Dep:为不同的属性创建唯一的watcherList,用来存放属性的监听者
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
Compile: 解析模板指令,绑定监听,替换数据,以及创建watcher(fragment)
bind: function(node, vm, exp, dir) {
var updaterFn = updater[dir + 'Updater'];
// 第一次初始化视图
updaterFn && updaterFn(node, vm[exp]);
// 实例化订阅者,此操作会在对应的属性消息订阅器中添加了该订阅者watcher
new Watcher(vm, exp, function(value, oldValue) {
// 一旦属性值有变化,会收到通知执行此更新函数,更新视图
updaterFn && updaterFn(node, value, oldValue);
});
}
watcher: 向Dep中添加自己,调用complie中传入给自己的回调函数
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
// 此处为了触发属性的getter,从而在dep添加自己,结合Observer更易理解
this.value = this.get();
}
Watcher.prototype = {
update: function() {
this.run(); // 属性值变化收到通知
},
run: function() {
var value = this.get(); // 取到最新值
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal); // 执行Compile中绑定的回调,更新视图
}
},
get: function() {
Dep.target = this; // 将当前订阅者指向自己
var value = this.vm[exp]; // 触发getter,添加自己到属性订阅器中
Dep.target = null; // 添加完毕,重置
return value;
}
};
最终入口(proxy)
observe(data, this);
this.$compile = new Compile(options.el || document.body, this)
网友评论