diff --git a/packages/reactivity/__tests__/computed.spec.ts b/packages/reactivity/__tests__/computed.spec.ts index c9f47720edd..f0681829bda 100644 --- a/packages/reactivity/__tests__/computed.spec.ts +++ b/packages/reactivity/__tests__/computed.spec.ts @@ -123,21 +123,6 @@ describe('reactivity/computed', () => { expect(getter2).toHaveBeenCalledTimes(2) }) - it('should no longer update when stopped', () => { - const value = reactive<{ foo?: number }>({}) - const cValue = computed(() => value.foo) - let dummy - effect(() => { - dummy = cValue.value - }) - expect(dummy).toBe(undefined) - value.foo = 1 - expect(dummy).toBe(1) - cValue.effect.stop() - value.foo = 2 - expect(dummy).toBe(1) - }) - it('should support setter', () => { const n = ref(1) const plusOne = computed({ @@ -219,12 +204,6 @@ describe('reactivity/computed', () => { expect(isReadonly(z.value.a)).toBe(false) }) - it('should expose value when stopped', () => { - const x = computed(() => 1) - x.effect.stop() - expect(x.value).toBe(1) - }) - it('debug: onTrack', () => { let events: DebuggerEvent[] = [] const onTrack = vi.fn((e: DebuggerEvent) => { diff --git a/packages/reactivity/__tests__/deferredComputed.spec.ts b/packages/reactivity/__tests__/deferredComputed.spec.ts index 8e78ba959c3..3df8859a7fc 100644 --- a/packages/reactivity/__tests__/deferredComputed.spec.ts +++ b/packages/reactivity/__tests__/deferredComputed.spec.ts @@ -136,20 +136,4 @@ describe('deferred computed', () => { c2.value expect(effectSpy).toHaveBeenCalledTimes(2) }) - - test('should not compute if deactivated before scheduler is called', () => { - const c1Spy = vi.fn() - const src = ref(0) - const c1 = computed(() => { - c1Spy() - return src.value % 2 - }) - effect(() => c1.value) - expect(c1Spy).toHaveBeenCalledTimes(1) - - c1.effect.stop() - // trigger - src.value++ - expect(c1Spy).toHaveBeenCalledTimes(1) - }) }) diff --git a/packages/reactivity/__tests__/effectScope.spec.ts b/packages/reactivity/__tests__/effectScope.spec.ts index f7e3241ccd6..28a1d4db87d 100644 --- a/packages/reactivity/__tests__/effectScope.spec.ts +++ b/packages/reactivity/__tests__/effectScope.spec.ts @@ -267,8 +267,8 @@ describe('reactivity/effect/scope', () => { r.value++ c!.value await nextTick() + expect(computedSpy).toHaveBeenCalledTimes(3) // should not trigger anymore - expect(computedSpy).toHaveBeenCalledTimes(2) expect(watchSpy).toHaveBeenCalledTimes(1) expect(watchEffectSpy).toHaveBeenCalledTimes(2) }) diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index a4b74172fcf..f75c7186851 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -59,7 +59,7 @@ export class ComputedRefImpl { ), ) this.effect.computed = this - this.effect.active = this._cacheable = !isSSR + this._cacheable = !isSSR this[ReactiveFlags.IS_READONLY] = isReadonly } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index ca90544c0de..398e35aba98 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -26,7 +26,6 @@ export type DebuggerEventExtraInfo = { export let activeEffect: ReactiveEffect | undefined export class ReactiveEffect { - active = true deps: Dep[] = [] /** @@ -39,7 +38,6 @@ export class ReactiveEffect { */ allowRecurse?: boolean - onStop?: () => void // dev only onTrack?: (event: DebuggerEvent) => void // dev only @@ -105,9 +103,6 @@ export class ReactiveEffect { run() { this._dirtyLevel = DirtyLevels.NotDirty - if (!this.active) { - return this.fn() - } let lastShouldTrack = shouldTrack let lastEffect = activeEffect try { @@ -123,6 +118,19 @@ export class ReactiveEffect { shouldTrack = lastShouldTrack } } +} + +export class ReactiveSideEffect extends ReactiveEffect { + active = true + + onStop?: () => void + + run() { + if (!this.active) { + return this.fn() + } + return super.run() + } stop() { if (this.active) { @@ -177,7 +185,7 @@ export interface ReactiveEffectOptions extends DebuggerOptions { export interface ReactiveEffectRunner { (): T - effect: ReactiveEffect + effect: ReactiveSideEffect } /** @@ -198,7 +206,7 @@ export function effect( fn = (fn as ReactiveEffectRunner).effect.fn } - const _effect = new ReactiveEffect(fn, NOOP, () => { + const _effect = new ReactiveSideEffect(fn, NOOP, () => { if (_effect.dirty) { _effect.run() } diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 6a3eaec9fd5..02601e3036a 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,4 +1,4 @@ -import type { ReactiveEffect } from './effect' +import type { ReactiveEffect, ReactiveSideEffect } from './effect' import { warn } from './warning' let activeEffectScope: EffectScope | undefined @@ -11,7 +11,7 @@ export class EffectScope { /** * @internal */ - effects: ReactiveEffect[] = [] + effects: (ReactiveEffect | ReactiveSideEffect)[] = [] /** * @internal */ @@ -82,7 +82,10 @@ export class EffectScope { if (this._active) { let i, l for (i = 0, l = this.effects.length; i < l; i++) { - this.effects[i].stop() + const effect = this.effects[i] + if ('stop' in effect) { + effect.stop() + } } for (i = 0, l = this.cleanups.length; i < l; i++) { this.cleanups[i]() diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 1c80fbc752b..43afe5d1792 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -54,6 +54,7 @@ export { pauseScheduling, resetScheduling, ReactiveEffect, + ReactiveSideEffect, type ReactiveEffectRunner, type ReactiveEffectOptions, type EffectScheduler, diff --git a/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts b/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts index 04e9c1c86db..4f4a87b2303 100644 --- a/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts +++ b/packages/runtime-core/__tests__/apiSetupHelpers.spec.ts @@ -1,6 +1,7 @@ import { type ComponentInternalInstance, type ComputedRef, + type ReactiveSideEffect, type SetupContext, Suspense, computed, @@ -13,6 +14,7 @@ import { render, serializeInner, shallowReactive, + watchEffect, } from '@vue/runtime-test' import { createPropsRestProxy, @@ -427,6 +429,7 @@ describe('SFC