1-什么时候初始化?
function Vue(){
... 其他处理
initState(this)
...解析模板,生成DOM 插入页面
}
function initState(vm) {
var opts = vm.$options;
if (opts.computed) {
initComputed(vm, opts.computed);
}
.....
}
你调用 Vue 创建实例过程中,会去处理各种选项,其中包括处理 computed
处理 computed 的方法是 initComputed
function initComputed(vm, computed) {
var watchers = vm._computedWatchers =
Object.create(null);
for (var key in computed) {
var userDef = computed[key];
var getter =
typeof userDef === 'function' ?
userDef: userDef.get;
// 每个 computed 都创建一个 watcher
// watcher 用来存储计算值,判断是否需要重新计算
watchers[key] =
new Watcher(vm, getter, {
lazy: true
});
// 判断是否有重名的属性
if (! (key in vm)) {
defineComputed(vm, key, userDef);
}
}
}
initComputed 这段代码做了几件事
1、每个 computed 配发 watcher
2、defineComputed 处理
3、收集所有 computed 的 watcher
好的,这三件事,一件一件说哈
4、每个 computed 配发 watcher
computed 到底和 watcher 有什么猫腻呢?
5、保存 computed 计算函数 2、保存计算结果 3、控制缓存计算结果是否有效
看下 Watcher 源码构造函数
function Watcher(vm, expOrFn, options) {
this.dirty = this.lazy = options.lazy;
this.getter = expOrFn;
this.value = this.lazy ? undefined: this.get();
};
new Watcher(vm, getter, { lazy: true })
watcher.value 存放计算结果,但是这里有个条件,因为 lazy 的原因,不会新建实例并马上读取值
这里可以算是 Vue 的一个优化,只有你再读取 computed,再开始计算,而不是初始化就开始计算值了
computed 新建 watcher 的时候,传入 lazy
没错,作用是把计算结果缓存起来,而不是每次使用都要重新计算
而这里呢,还把 lazy 赋值给了 dirty,为什么呢?
因为 lazy 表示一种固定描述,不可改变,表示这个 watcher 需要缓存
而 dirty 表示缓存是否可用,如果为 true,表示缓存脏了,需要重新计算,否则不用
dirty 默认是 false 的,而 lazy 赋值给 dirty,就是给一个初始值,表示 你控制缓存的任务开始了
所以记住,【dirty】 是真正的控制缓存的关键,而 lazy 只是起到一个开启的作用
具体,怎么控制缓存,下面会说
defineComputed 处理
function defineComputed(
target, key, userDef
) {
// 设置 set 为默认值,避免 computed 并没有设置 set
var set = function(){}
// 如果用户设置了set,就使用用户的set
if (userDef.set) set = userDef.set
Object.defineProperty(target, key, {
// 包装get 函数,主要用于判断计算缓存结果是否有效
get:createComputedGetter(key),
set:set
});
}
1、使用 Object.defineProperty 在 实例上computed 属性,所以可以直接访问
2、set 函数默认是空函数,如果用户设置,则使用用户设置
3、createComputedGetter 包装返回 get 函数
function createComputedGetter(key) {
return function() {
// 获取到相应 key 的 computed-watcher
var watcher = this._computedWatchers[key];
// 如果 computed 依赖的数据变化,dirty 会变成true,
从而重新计算,然后更新缓存值 watcher.value
if (watcher.dirty) {
watcher.evaluate();
}
// 这里是 月老computed 牵线的重点,让双方建立关系
if (Dep.target) {
watcher.depend();
}
return watcher.value
}
}
自此,讲新的数据返回