# Computed
了解完Ref和Effect再来看Computed就比较简单了,Vue3.x的Computed和2.x的使用方法类似,它的核心就是基于Effect实现的。 当计算Computed的值时,会收集Computed中函数中的依赖,当依赖改变时,再次访问Computed会重新计算结果并返回。
// 从computed方法的参数类型看到,computed支持传入一个函数或者是一个配置了getter、setter的对象
// 从computed方法的返回类型可以看到,computed本身是一个Ref类型的对象
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 当传入的getterOrOptions是一个函数时,赋值给变量getter
// 当传入的getterOrOptions是对象时,拿到get、set方法赋值给getter、setter
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 设置变量dirty,如果dirty为true则代表应该重新计算computed的值
let dirty = true
let value: T
let computed: ComputedRef<T>
// 创建一个Effect并且将getter作为参数传入
const runner = effect(getter, {
// 配置lazy为true,所以effect创建的时候不会执行
lazy: true,
// 设置了scheduler方法,所以当getter中依赖的响应式对象改变时,会执行scheduler而不是effect本身
scheduler: () => {
// scheduler并没有重新计算computed的值
// 而是将dirty变为true,当dirty为true时代表computed的值已经发生改变
// 所以也要派发更新到其他依赖了computed的Effect
if (!dirty) {
dirty = true
trigger(computed, TriggerOpTypes.SET, 'value')
}
}
})
// computed本身是一个Ref类型的对象
computed = {
__v_isRef: true,
[ReactiveFlags.IS_READONLY]:
isFunction(getterOrOptions) || !getterOrOptions.set,
// expose effect so computed can be stopped
effect: runner,
// 当访问value时,如果dirty为true,重新执行runner返回getter的结果
// 并调用track添加依赖
get value() {
if (dirty) {
value = runner()
dirty = false
}
track(computed, TrackOpTypes.GET, 'value')
return value
},
// 当直接设置value的值时,如果没有传入set方法,在开发环境中会有警告
set value(newValue: T) {
setter(newValue)
}
} as any
return computed
}
# 总结
分析完了Computed的实现,可以总结出Computed有以下特性:
本身是一个Ref类型的对象,所以拥有Ref类型对象的特性,比如响应式、嵌套在响应式对象中默认展开等
创建或者Computed依赖的值发生改变时,并不会立即重新计算,而是等到下次访问的时候才重新计算Computed的值
← effect 创建一个Vue3.x应用 →