前言
在初始化过程中initState主要负责初始化props、methods、data、computed与watch,此篇主要对computed与watch初始化的源码解析。
data、methods、props的部分前面已有,
data导航:vue2.0源码解析,Data
methods导航:vue2.0源码解析,Methods
initRender导航: vue2.0源码解析,initRender
initProps导航: vue2.0源码解析,initState之initProps
前置知识
computed、watch都是借助了vue的响应式原理来实现功能,所以读者需要知道vue的观察者模式大致的实现原理,可帮助更容易理解接下来的内容。
本人前面也有一篇文章介绍了简单的实现原理,导航:实现vue2.0响应式原理
话不多说,直接开撸
initComputed部分
initComputed
export function noop () {
};
const computedWatcherOptions = {
lazy: true };
/*初始化computed*/
function initComputed(vm: Component, computed: Object) {
const watchers = (vm._computedWatchers = Object.create(null));
for (const key in computed) {
const userDef = computed[key];
/*
计算属性可能是一个function,也有可能设置了get以及set的对象。
可以参考 https://cn.vuejs.org/v2/guide/computed.html#计算-setter
*/
let getter = typeof userDef === 'function' ? userDef : userDef.get;
// create internal watcher for the computed property.
/*
为计算属性创建一个内部的监视器Watcher,保存在vm实例的_computedWatchers中
这里的computedWatcherOptions参数传递了一个lazy为true,会使得watch实例的dirty为true
*/
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
/*组件正在定义的计算属性已经定义在现有组件的原型上则不会进行重复定义*/
if (!(key in vm)) {
/*定义计算属性*/
defineComputed(vm, key, userDef);
}
}
}
首先拿到computed定义的所有子值,判断是否直接赋值还是从get里面拿(vue支持两种定义方法,见下图)
在这里创建了一个Watcher对象,并且传入lazy=true的option,并且回调函数为空。
最后一段代码做了一个判断当vm对象里面没有同名方法或属性时,执行defineComputed。
继续往下看
defineComputed
/*定义计算属性*/
export function defineComputed(target: any, key: string, userDef: Object | Function) {
if (typeof userDef === 'function') {
/*创建计算属性的getter*/
sharedPropertyDefinition.get = createComputedGetter(key);
/*
当userDef是一个function的时候是不需要setter的,所以这边给它设置成了空函数。
因为计算属性默认是一个function,只设置getter。
当需要设置setter的时候,会将计算属性设置成一个对象。参考:https://cn.vuejs.org/v2/guide/computed.html#计算-setter
*/
sharedPropertyDefinition.set = noop;
} else {
/*get不存在则直接给空函数,如果存在则查看是否有缓存cache,没有依旧赋值get,有的话使用createComputedGetter创建*/
const cacheGetter = userDef.cache !== false ? createComputedGetter(key) : userDef.get
sharedPropertyDefinition.get = userDef.get ? cacheGetter : noop;
/*如果有设置set方法则直接使用,否则赋值空函数*/
sharedPropertyDefinition.set = userDef.set ? userDef.set : noop;
}
/*defineProperty上getter与setter*/
Object.defineProperty(target, key, sharedPropertyDefinition);
}
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop,
};
传进来的userDef就是computed里面拿到的value,这里首先判断根据传入的是一个函数或者对象来定义,最后使用了Object.defineProperty将他挂载到VM对象上(我们使用的this.对应的key)。
定义 sharedPropertyDefinition 变量也只是为了方便重用,定义好了可读可写的options,使用时直接设置get、set方法就行。
在esle里面,有一个userDef.cache的三元判断,userDef.cache !== false是一个全等判断,如果不特意设置为false,会走到第一个条件。而computed有一个重要特性就是会收集依赖并缓存get结果,以节省性能,等待依赖更新后才会重新get。而vue为了给内部使用留出cache设置,官方文档没有展示,我们自己使用时也可以设置cache:false不使用结果缓存。
createComputedGetter