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)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。