- : 只用fullName去值,才会计算传入computed的回调。
- 先收集fullName的effect,再收集firstName的effect。
测试用例:
const {reactive, effect} = VueReactivity
const state = reactive({ firstName: 'l'})
let fullName = computed(()=>{
return state.firstName
})
effect(()=>{
app.innerHTML = fullName.value;
})
setTimeout(()=>{
state.firstName = "路"
})
computed
export function computed<T>(getterOrOptions) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
const onlyGetter = isFunction(getterOrOptions)
if (onlyGetter) {
getter = getterOrOptions
setter = NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter)
return cRef as any
}
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 = false
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)
}
})
this.effect.computed = this
this.effect.active = this._cacheable = !isSSR
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
const self = 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)
}
}
- state.firstName = "路",更改firstName 会触发对应的effect,执行triggerEffects,firstName对应的effect的effect.computed存在,执行相应的triggerEffect。
- 此时effect.scheduler 存在,执行scheduler,会执行 triggerRefValue(this)即执行fullName对应的effect的trigger.执行effect.run().去取fullName.value的值。
- 之后触发 fullName的get函数,执行trackRefValue(self),对fullName的这个effect进行收集。
- 再去执行self._value = self.effect.run()!,去执行firstName对应的effect.run,截止执行this.fn()即computed传入的回调函数,取firstName的值,对此effect进行收集,并将函数计算的新值,传入self._value,此时就完成了更新。
export function triggerEffects(dep) {
const effects = isArray(dep) ? dep : [...dep]
for (const effect of effects) {
if (effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
for (const effect of effects) {
if (!effect.computed) {
triggerEffect(effect, debuggerEventExtraInfo)
}
}
}
function triggerEffect(effect: ReactiveEffect) {
if (effect !== activeEffect || effect.allowRecurse) {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
}
测试用例
const tick = Promise.resolve()
const src = ref(0)
const c1 = deferredComputed(() => {
console.log("c1")
return src.value % 2
})
const c2 = computed(() => {
console.log("c2")
return c1.value + 1
})
effect(() => {
console.log('effect')
console.log(c2.value)
})
src.value = 1
await tick
constructor(getter: ComputedGetter<T>) {
let compareTarget: any
let hasCompareTarget = false
let scheduled = false
this.effect = new ReactiveEffect(getter, (computedTrigger?: boolean) => {
if (this.dep) {
if (computedTrigger) {
compareTarget = this._value
hasCompareTarget = true
} else if (!scheduled) {
const valueToCompare = hasCompareTarget ? compareTarget : this._value
scheduled = true
hasCompareTarget = false
scheduler(() => {
if (this.effect.active && this._get() !== valueToCompare) {
triggerRefValue(this)
}
scheduled = false
})
}
}
this._dirty = true
})
this.effect.computed = this as any
}
- src.value = 1,执行对应的trigger-->triggerEffects---> src.value收集的effect是DeferredComputedRefImpl.effect,此effect包含computed属性,---> triggerEffect---->此effect有scheduler函数,执行scheduler函数。将回调函数传入queue中,就执行完了。
- 等到await tick执行完后,执行传入到queue中的回到函数。
- 执行this._get(),执行this.effect.run()---->执行传入deferredComputed的回调,在控制台打印c1,取值src.value---->使src.value收集这个deferredComputed的activeEffect
- 执行完this._get后,执行triggerRefValue(this),此dep中存放的是使用c1的activeEffect,---->执行trigger---->triggerEffects----->triggerEffect--->此activeEffect.scheduler存在,执行这个scheduler,执行triggerRefValue(this),此activeEffect.dep 存放使用c2的activeEffect,
- activeEffect.run---->this.fn---->控制台打印effect,c2.value取值,执行get方法,
- 使coumputed的dep收集activeEffect,再执行self._value = self.effect.run()!,
拿到c2.vlaue的值。
private _get() {
if (this._dirty) {
this._dirty = false
return (this._value = this.effect.run()!)
}
return this._value
}
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
get value() {
const self = toRaw(this)
trackRefValue(self)
if (self._dirty || !self._cacheable) {
self._dirty = false
self._value = self.effect.run()!
}
return self._value
}