fix(lifecycle): scope might changed when call hook (#13070)

This commit is contained in:
Red Huang 2023-10-22 10:52:10 +08:00 committed by GitHub
parent 1399ee6aa0
commit 74ca5a13ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 2 deletions

View File

@ -18,6 +18,7 @@ import {
invokeWithErrorHandling invokeWithErrorHandling
} from '../util/index' } from '../util/index'
import { currentInstance, setCurrentInstance } from 'v3/currentInstance' import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
import { getCurrentScope } from 'v3/reactivity/effectScope'
import { syncSetupProxy } from 'v3/apiSetup' import { syncSetupProxy } from 'v3/apiSetup'
export let activeInstance: any = null export let activeInstance: any = null
@ -398,7 +399,8 @@ export function callHook(
) { ) {
// #7573 disable dep collection when invoking lifecycle hooks // #7573 disable dep collection when invoking lifecycle hooks
pushTarget() pushTarget()
const prev = currentInstance const prevInst = currentInstance
const prevScope = getCurrentScope()
setContext && setCurrentInstance(vm) setContext && setCurrentInstance(vm)
const handlers = vm.$options[hook] const handlers = vm.$options[hook]
const info = `${hook} hook` const info = `${hook} hook`
@ -410,6 +412,10 @@ export function callHook(
if (vm._hasHookEvent) { if (vm._hasHookEvent) {
vm.$emit('hook:' + hook) vm.$emit('hook:' + hook)
} }
setContext && setCurrentInstance(prev) if (setContext) {
setCurrentInstance(prevInst)
prevScope && prevScope.on()
}
popTarget() popTarget()
} }

View File

@ -1,3 +1,4 @@
import Vue from 'vue'
import { nextTick } from 'core/util' import { nextTick } from 'core/util'
import { import {
watch, watch,
@ -290,4 +291,28 @@ describe('reactivity/effectScope', () => {
expect(getCurrentScope()).toBe(parentScope) expect(getCurrentScope()).toBe(parentScope)
}) })
}) })
it('scope should not break currentScope when component call hooks', () => {
const scope = new EffectScope()
const vm = new Vue({
template: `
<div>
<div v-if="show" />
</div>
`,
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)
})
}) })