详解Vue计算属性原理
作者:WeilinerL
初始化
组件初始化时会调用挂载在Vue原型链上的_init
方法,即Vue.prototype._init
:
function Vue(options) { this._init(options) }
同时_init
方法会调用initState
方法,这个方法主要用来初始化props、methods、data、watch以及本文所介绍的computed:
Vue.prototype._init = function (options?: Record<string, any>) { // ...省略一些代码 initState(vm) // ...省略一些代码 }
为了看上去一目了然,这里省略了一些代码(包括判断条件)
export function initState(vm: Component) { // ..省略了一些代码 initProps(vm, opts.props) initMethods(vm, opts.methods) initData(vm) initComputed(vm, opts.computed) initWatch(vm, opts.watch) }
走到这里之后,程序就进入了初始化计算属性的过程中
initComputed
在这个方法里,主要做了两件事:
- 遍历用户传入的计算属性,生成相应的
Wacher
,每个Watcher
都被标记为lazy
- 在组件实例
vm
上挂载同名属性,值为一个按条件生成的getter
函数
以下代码有省略
function initComputed(vm: Component, computed: Object) { const watchers = (vm._computedWatchers = Object.create(null)) for (const key in computed) { const userDef = computed[key]; // 获取用户自定义的计算属性getter const getter = isFunction(userDef) ? userDef : userDef.get; watchers[key] = new Watcher( vm, getter || noop, noop, { lazy: true } ); defineComputed(vm, key, userDef); } }
其中new Watcher
会生成Watcher
实例:
export default class Watcher implements DepTarget { constructor( vm: Component | null, expOrFn: string | (() => any), cb: Function, options?: WatcherOptions | null, isRenderWatcher?: boolean ) { this.getter = expOrFn this.value = this.lazy ? undefined : this.get(); }
因为我们传入的lazy
配置,Wacher不会执行Watcher.prototype.get()
方法,这个方法主要用于依赖收集
defineComputed
这个方法主要是在组件实例上生成相应的计算属性,便于我们在组件内部通过this[key]
的方式进行获取,主要代码如下:
export function defineComputed( target: any, key: string, userDef: Record<string, any> | (() => any) ) { sharedPropertyDefinition.get = createComputedGetter(key); Object.defineProperty(target, key, sharedPropertyDefinition) }
这里的target
就是组件实例,key
即为我们定义的相应的响应式属性名,它的值是一个 生成的getter
;
createComputedGetter
有了访问属性的方式,那么就需要返回相应的计算属性值,这个方法主要用来实现计算属性的惰性求值的:
function createComputedGetter(key) { return function computedGetter() { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() } // ... return watcher.value } } }
这里返回的函数就是用于设置Object.defineProperty
的getter的存取描述符get
,它会根据watcher.dirty
,判断是否需要调用方法watcher.evaluate()
进行求值。 在watcher.evaluate
方法里主要做两件事:
- 调用
get
方法进行依赖收集(会调用我们在计算属性上定义的函数,从而触发相应的响应式数据的getter进行依赖收集) - 将
watcher
的dirty
标志位置为false
this.value = this.get() this.dirty = false
完成了上述步骤后,整个响应式属性就建立完毕,当计算属性依赖的数据发生变化时,会调用watcher.update()
方法,这个方法会再次将dirty
标志位写为false
。当我们在模版里或其他地方访问组件实例上的响应式属性后,就会触发上述定义的createComputedGetter
返回的函数。从而进行wacher.evaluate()
求值进行重新计算,计算完成后又将dirty
置为false
,等待下一次重新计算。如果我们不妨问这个属性,那么是不会触发getter,从而进行计算的(惰性求值)。
总结
方法调用流程:
组件初始化 -> _init()
-> initState
-> initComputed
-> definedComputed
-> createComputedGetter
数据流:
watcher.evaluate
-> 依赖收集 -> dirty = false
-> update
-> dirty = true
-> 用户访问计算属性 -> watcher.evaluate
-> 循环此过程...
纸上得来终觉浅 绝知此事要躬行
以上就是详解Vue计算属性原理的详细内容,更多关于Vue计算属性原理的资料请关注脚本之家其它相关文章!