1. 首先从 computed 钻研开始
function initComputed (vm: Component, computed: Object) {
// 首先应用连等形式,申明 watchers,并给 vue 实例上增加_computedWatchers 属性,二者指向同一对象,用来记录所有的 computed
const watchers = vm._computedWatchers = Object.create(null)
// 判断是否是服务端渲染
const isSSR = isServerRendering()
for (const key in computed) {const userDef = computed[key]
// computed 两种申明形式函数、对象:{get,set}
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (!isSSR) {
// create internal watcher for the computed property.
// 给每一个 computed 创立 Watcher 实例,并增加到 watchers,vm._computedWatchers 也会增加
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// 判断 vm 上是否已存在雷同属性名,存在报错,不存在代理到 vm 上,以便 this.xxx 调用
if (!(key in vm)) {defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {if (key in vm.$data) {warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {warn(`The computed property "${key}" is already defined as a prop.`, vm)
} else if (vm.$options.methods && key in vm.$options.methods) {warn(`The computed property "${key}" is already defined as a method.`, vm)
}
}
}
}
2.defineComputed
1) 首先看下 noop 函数是什么?
// noop 就是一个空函数
export function noop (a?: any, b?: any, c?: any) {}
2) 看一下 createComputedGetter 函数
function createComputedGetter (key) {// 例: computedName(){return '我的名字是' + this.name}
// 返回一个 getter 函数,将 computed 属性 (computedName) 的 get 指向到 watcher.value,
// 这里的 watcher.value 会触发 watcher.prototype.get 办法,进而触发 computed 中依赖响应式变量的 get 办法,// 而后触发 dep.depend(), 将 watcher 增加到该变量闭包中 dep 实例的 subs 数组
return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
// computed 的默认会传 lazy:true, watcher.dirty 初始值等于 lazy,所以这里是 true
if (watcher.dirty) {//this.value 获取值, 并且把 dirty 设置为 false,这样就能够起到缓存的作用,屡次拜访同一个 computed,只会触发一次 watcher.get()
// evaluate () {// this.value = this.get()
// this.dirty = false
// }
watcher.evaluate() // 翻译是估值,求函数的值}
if (Dep.target) {watcher.depend()
}
return watcher.value
}
}
}
3) 最初看一下最终的 defineComputed 函数,这里会用到 noop 和 createComputedGetter,所以在下面提前理解一下
// 例: computedName(){return '我的名字是' + this.name}
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
// 判断是否是服务端渲染,这里只钻研客户端渲染的状况
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {// 客户端渲染时 shouldCache 为 true, 也就是会将 computedName.get 设置为 createComputedGetter(),
// createComputedGetter 会返回一个 getter 办法,见下方
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
// 如果 computed 属性是 function 时,被设置时会报错,这就是咱们平时为什么 computed 不能被设置的起因
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {sharedPropertyDefinition.set = function () {
warn(`Computed property "${key}" was assigned to but it has no setter.`,
this
)
}
}
// 将 computed 属性 (computedName) 定义到 vm 上,这里的 target 就是 vm(vue 实例)
Object.defineProperty(target, key, sharedPropertyDefinition)
}