From: Evan You Date: Wed, 8 Jan 2025 10:07:44 +0000 (+0800) Subject: fix(reactivity): ensure multiple effectScope on() and off() calls maintains correct... X-Git-Tag: v3.5.14~113 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=22dcbf3e20eb84f69c8952f6f70d9990136a4a68;p=thirdparty%2Fvuejs%2Fcore.git fix(reactivity): ensure multiple effectScope on() and off() calls maintains correct active scope close #12631 close #12632 This is a combination of changes from both 8dec243 and #12641 --- diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index cb4e057c48..92ad92c124 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -8,6 +8,10 @@ export class EffectScope { * @internal */ private _active = true + /** + * @internal track `on` calls, allow `on` call multiple times + */ + private _on = 0 /** * @internal */ @@ -99,12 +103,16 @@ export class EffectScope { } } + prevScope: EffectScope | undefined /** * This should only be called on non-detached scopes * @internal */ on(): void { - activeEffectScope = this + if (++this._on === 1) { + this.prevScope = activeEffectScope + activeEffectScope = this + } } /** @@ -112,7 +120,10 @@ export class EffectScope { * @internal */ off(): void { - activeEffectScope = this.parent + if (this._on > 0 && --this._on === 0) { + activeEffectScope = this.prevScope + this.prevScope = undefined + } } stop(fromParent?: boolean): void { diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 7d2a1e73c0..39032a6369 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -31,6 +31,7 @@ import { TrackOpTypes, TriggerOpTypes, effectScope, + onScopeDispose, shallowReactive, shallowRef, toRef, @@ -1982,4 +1983,31 @@ describe('api: watch', () => { expect(spy1).toHaveBeenCalled() expect(spy2).toHaveBeenCalled() }) + + // #12631 + test('this.$watch w/ onScopeDispose', () => { + const onCleanup = vi.fn() + const toggle = ref(true) + + const Comp = defineComponent({ + render() {}, + created(this: any) { + this.$watch( + () => 1, + function () {}, + ) + onScopeDispose(onCleanup) + }, + }) + + const App = defineComponent({ + render() { + return toggle.value ? h(Comp) : null + }, + }) + + const root = nodeOps.createElement('div') + createApp(App).mount(root) + expect(onCleanup).toBeCalledTimes(0) + }) })