From 74ca5a13ba12a31580f1567e7c6d789e96730e46 Mon Sep 17 00:00:00 2001 From: Red Huang Date: Sun, 22 Oct 2023 10:52:10 +0800 Subject: [PATCH] fix(lifecycle): scope might changed when call hook (#13070) --- src/core/instance/lifecycle.ts | 10 ++++++-- .../v3/reactivity/effectScope.spec.ts | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts index 94f42e27e..d1b5a76d9 100644 --- a/src/core/instance/lifecycle.ts +++ b/src/core/instance/lifecycle.ts @@ -18,6 +18,7 @@ import { invokeWithErrorHandling } from '../util/index' import { currentInstance, setCurrentInstance } from 'v3/currentInstance' +import { getCurrentScope } from 'v3/reactivity/effectScope' import { syncSetupProxy } from 'v3/apiSetup' export let activeInstance: any = null @@ -398,7 +399,8 @@ export function callHook( ) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() - const prev = currentInstance + const prevInst = currentInstance + const prevScope = getCurrentScope() setContext && setCurrentInstance(vm) const handlers = vm.$options[hook] const info = `${hook} hook` @@ -410,6 +412,10 @@ export function callHook( if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } - setContext && setCurrentInstance(prev) + if (setContext) { + setCurrentInstance(prevInst) + prevScope && prevScope.on() + } + popTarget() } diff --git a/test/unit/features/v3/reactivity/effectScope.spec.ts b/test/unit/features/v3/reactivity/effectScope.spec.ts index 6b837e67c..78966e42e 100644 --- a/test/unit/features/v3/reactivity/effectScope.spec.ts +++ b/test/unit/features/v3/reactivity/effectScope.spec.ts @@ -1,3 +1,4 @@ +import Vue from 'vue' import { nextTick } from 'core/util' import { watch, @@ -290,4 +291,28 @@ describe('reactivity/effectScope', () => { expect(getCurrentScope()).toBe(parentScope) }) }) + + it('scope should not break currentScope when component call hooks', () => { + const scope = new EffectScope() + const vm = new Vue({ + template: ` +
+
+
+ `, + data() { + return { + show: false + } + } + }).$mount() + + scope.run(() => { + // call renderTriggered hook here + vm.show = true + // this effect should be collected by scope not the component scope + effect(() => {}) + }) + expect(scope.effects.length).toBe(1) + }) })