步骤一:
computed函数定义在packages/src/reactivity/computed.ts文件中:
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions,
isSSR = false
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// getterOrOptions 即传入的函数
const onlyGetter = isFunction(getterOrOptions) // 判断 getterOrOptions 是否为函数
if (onlyGetter) {
getter = getterOrOptions // 赋值 传入的函数
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly') // 理解为空函数
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 创建 ComputedRefImpl 实例
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)
if (__DEV__ && debugOptions && !isSSR) {
cRef.effect.onTrack = debugOptions.onTrack
cRef.effect.onTrigger = debugOptions.onTrigger
}
// 返回 实例
return cRef as any
}
computed 函数接收一个 getterOrOptions 参数,即传入的匿名函数
步骤二:
创建一个ComputedRefImpl实例,构造函数会创建一个ReactiveEffect实例。
export class ComputedRefImpl<T> {
public dep?: Dep = undefined
private _value!: T
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
public readonly [ReactiveFlags.IS_READONLY]: boolean
public _dirty = true // 脏变量 关键
public _cacheable: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean,
isSSR: boolean
) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this) // dirty 为 false 时 触发依赖
}
})
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
const self = toRaw(this)
trackRefValue(self) // 依赖收集
if (self._dirty || !self._cacheable) {
self._dirty = false
self._value = self.effect.run()!
}
return self._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
步骤三:执行effect函数
总结:
computed计算属性实际是一个ComputedRefImpl构造函数的实例ComputedRefImpl构造函数中通过dirty变量控制effect中run方法的执行和triggerRefValue的触发- 先触发
computed的effect,再触发非computed的effect【避免多次.value赋值时造成死循环】
- 先触发
- 每次访问计算属性的值,必须通过
.value,执行get value方法,触发trackRefValue`进行依赖收集