疑问:data中的变量只有在template或者render中使用才会收集依赖
构造demo
先构造一个最简单的demo
import Vue from "vue";
new Vue({
data: {
count: 0,
},
render() {
return (
<div>
<h1 onClick={this.add}>{this.dcount}</h1>
</div>
);
},
computed: {
dcount() {
return this.count * 2;
},
},
methods: {
add() {
this.count++;
},
},
}).$mount("#app");
每次点击修改count视图都会更新,render函数中并没有使用count, 显然这个说法不正确。
那么怎么解析这个demo呢?
原因分析
通过断点调试vue,我们可以知道new Vue中的一个流程,上面的demo主要分析流程
initData通过defineProperty设置count的getter、setter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
});
$mount('#app')实例化渲染类型的watcher,立刻求值,执行render函数,此时会触发dcount的getter
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);
// updateComponent() ---> render()
// renderWatcher, 会立刻求值this.get();
// pushTarget
// Dep.target = renderWatcher;
// targetStack入栈 此时 targetStack = [ renderWatcher ]
render() {
return (
<div>
<h1 onClick={this.add}>{this.dcount}</h1> // 读取dcount,触发下面的computedGetter
</div>
);
},
initComupted构造一个computed类型的watcher,它不会立刻求值,在执行render时候才会去求值, watcher的dirty属性此时是true
function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
// 渲染watcher执行render的时候,触发computedGetter
if (watcher.dirty) {
// targetStack入栈, 此时Dep.tatget = watcher
// targetStack = [ renderWatcher, watcher];
// 触发watcher.get 实际上就是dcount函数
watcher.evaluate();
// tagetStack出栈,
// 此时Dep.target = renderWatcher
// targetStack = [ renderWatcher ]
// 计算结束后设置dirty为false, 这样子即使template中多次出现的dcount,
// 不需要重复计算,直接返回结果
}
// 这里十分关键,把renderWatch混入
if (Dep.target) { // 此时为renderWatcher
// 这个函数就是遍历watcher中的deps,把renderWatcher push到subs
// 因此count属性多了一个renderwatcher,每次修改count都使renderWatcher.update
watcher.depend();
}
return watcher.value // 返回结果
}
}
dcount依赖count,触发count的getter,当前的Dep.target是dcount实例的comupted-watcher
// 读取count,触发getter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); // 把Dep.target push到dep的subs
}
return value
},
});
流程图
当我点击h1标签的时候,读取this.count会触发count的getter, 可以看到subs中存在两个watcher.
总结
上面除了这种常见外,如果主动使用watch,如下
count属性的依赖dep中就包含了一个依赖, 因为使用watch也会创建一个watcher,且把watcher主动push到subs中。
import Vue from "vue";
new Vue({
data: {
count: 0,
},
render() {
return (
<div>
<h1 onClick={this.add}></h1>
</div>
);
},
watch: {
count() {
}
},
methods: {
add() {
this.count++;
},
},
}).$mount("#app");