详解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计算属性原理的资料请关注脚本之家其它相关文章!
