vue源码watcher阅读记录

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 = https://www.it610.com/article/this.get() //this.dirty = false // } watcher.evaluate() // 翻译是估值,求函数的值 } if (Dep.target) { watcher.depend() } return watcher.value } } }

【vue源码watcher阅读记录】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) }

    推荐阅读