From 280baf0840746a48737b825803bcf53b0f5d7528 Mon Sep 17 00:00:00 2001 From: zhaoyr <543434337@qq.com> Date: Sun, 25 Sep 2022 03:59:52 +0800 Subject: [PATCH 1/2] fix(reactivity): ensure get the latest value when reading the computed value --- .../__tests__/deferredComputed.spec.ts | 26 ++++++++++++++++ packages/reactivity/src/computed.ts | 31 +++++++++++++------ packages/reactivity/src/deferredComputed.ts | 2 +- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/reactivity/__tests__/deferredComputed.spec.ts b/packages/reactivity/__tests__/deferredComputed.spec.ts index 3940280176a..da1bd7b7e3a 100644 --- a/packages/reactivity/__tests__/deferredComputed.spec.ts +++ b/packages/reactivity/__tests__/deferredComputed.spec.ts @@ -166,6 +166,32 @@ describe('deferred computed', () => { expect(effectSpy).toHaveBeenCalledTimes(2) }) + test('sync access to computed property should get the latest value', () => { + const bSpy = jest.fn() + const c1Spy = jest.fn() + const c2Spy = jest.fn() + + const a = ref(0) + const b = deferredComputed(() => { + bSpy() + return a.value + }) + const c1 = deferredComputed(() => { + c1Spy() + return b.value + }) + const c2 = computed(() => { + c2Spy() + return b.value + }) + // emit computed getter + c1.value;c2.value; + a.value = 2 + // sync access + expect(c1.value).toEqual(2) + expect(c2.value).toEqual(2) + }) + test('should not compute if deactivated before scheduler is called', async () => { const c1Spy = jest.fn() const src = ref(0) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index e9f36ecd22f..15b8fb4ba2d 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -41,26 +41,37 @@ export class ComputedRefImpl { isReadonly: boolean, isSSR: boolean ) { - this.effect = new ReactiveEffect(getter, () => { - if (!this._dirty) { - this._dirty = true - triggerRefValue(this) + this.effect = new ReactiveEffect( + getter, + (deferredComputedTrigger?: boolean) => { + if (deferredComputedTrigger) { + this._dirty = true + }else{ + if (!this._dirty) { + this._dirty = true + } + triggerRefValue(this) + } } - }) + ) this.effect.computed = this this.effect.active = this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly } + private _get() { + if (this._dirty || !this._cacheable) { + this._dirty = false + return (this._value = this.effect.run()!) + } + return this._value + } + 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 + return self._get() } set value(newValue: T) { diff --git a/packages/reactivity/src/deferredComputed.ts b/packages/reactivity/src/deferredComputed.ts index a23122046a4..fd6a741c4aa 100644 --- a/packages/reactivity/src/deferredComputed.ts +++ b/packages/reactivity/src/deferredComputed.ts @@ -58,7 +58,7 @@ class DeferredComputedRefImpl { // value invalidation in case of sync access; normal effects are // deferred to be triggered in scheduler. for (const e of this.dep) { - if (e.computed instanceof DeferredComputedRefImpl) { + if (e.computed) { e.scheduler!(true /* computedTrigger */) } } From cf1d25910cf8aa1d87ed5e94749e7724084b035b Mon Sep 17 00:00:00 2001 From: zhaoyr <543434337@qq.com> Date: Sun, 25 Sep 2022 07:33:19 +0800 Subject: [PATCH 2/2] refactor(reactivity): restore unneeded changes --- packages/reactivity/src/computed.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index 15b8fb4ba2d..38fe213f039 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -59,19 +59,15 @@ export class ComputedRefImpl { this[ReactiveFlags.IS_READONLY] = isReadonly } - private _get() { - if (this._dirty || !this._cacheable) { - this._dirty = false - return (this._value = this.effect.run()!) - } - return this._value - } - get value() { // the computed ref may get wrapped by other proxies e.g. readonly() #3376 const self = toRaw(this) trackRefValue(self) - return self._get() + if (self._dirty || !self._cacheable) { + self._dirty = false + self._value = self.effect.run()! + } + return self._value } set value(newValue: T) {