vue.js动态数据绑定学习笔记

2020-06-13 10:42:10易采站长站整理

// 这一步相当于做了这么一件事:this.subs.push(Dep.target)
// 即添加了 Watcher 订阅,addDep 是 Watcher 的方法
Dep.target.addDep(this);
};

// 通知更新
Dep.prototype.notify = function() {
// this.subs 的每一项都为一个 Watcher 实例
this.subs.forEach(function(sub) {
// update 为 Watcher 的一个方法,更新视图
// 没错,实际上这个方法最终会调用到 Compile 中的 updaterFn,
// 也即 new Watcher(vm, exp, callback) 中的 callback
sub.update();
});
};

// 在 Watcher 中调用
Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
};

// 初始时引用为空
Dep.target = null;

也许看到这还是一脸懵逼,没关系,接着往下。大概有同学会疑惑,为什么要把添加 Watcher 订阅放在 getter 中,接下来我们来说说这 Watcher 和 Dep 的故事。

Watcher & Dep

先让我们回顾一下 Compile 做的事,解析 fragment,然后给相应属性添加订阅:new Watcher(vm, exp, cb)。new 了这个 Watcher 之后,Watcher 怎么办呢,就有了下面这样的对话:

Watcher:hey Dep,我需要订阅 exp 属性的变动。

Dep:这我可做不到,你得去找 exp 属性中的 dep,他能做到这件事。

Watcher:可是他在闭包中啊,我无法和他联系。

Dep:你拿到了整个 Hue 实例 vm,又知道属性 exp,你可以触发他的 getter 啊,你在 getter 里动些手脚不就行了。

Watcher:有道理,可是我得让 dep 知道是我订阅的啊,不然他通知不到我。

Dep:这个简单,我帮你,你每次触发 getter 前,把你的引用告诉 Dep.target 就行了。记得办完事后给 Dep.target 置空。

于是就有了上面 getter 中的代码:


// ...
get: function() {
// 是否是 Watcher 触发的
if (Dep.target) {
// 是就添加进来
dep.depend();
}
return val;
}
// ...

现在再回头看看 Dep 部分的代码,是不是好理解些了。如此一来, Watcher 需要做的事情就简单明了了:


function Watcher(vm, exp, cb) {
this.$vm = vm;
this.cb = cb;
this.exp = exp;
this.depIds = new Set();

// 返回一个用于获取相应属性值的函数
this.getter = parseGetter(exp.trim());

// 调用 get 方法,触发 getter
this.value = this.get();
}

Watcher.prototype.get = function() {
const vm = this.$vm;
// 将 Dep.target 指向当前 Watcher 实例
Dep.target = this;
// 触发 getter
let value = this.getter.call(vm, vm);
// Dep.target 置空
Dep.target = null;
return value;
};

Watcher.prototype.addDep = function(dep) {
const id = dep.id;
if (!this.depIds.has(id)) {