mirror of https://github.com/vuejs/core.git
fix(runtime-core): do not fire mount/activated hooks if unmounted before mounted (#9370)
close #8898 close #9264 close #9617
This commit is contained in:
parent
32262a9af5
commit
aa156ed5c4
|
@ -1,8 +1,10 @@
|
||||||
import {
|
import {
|
||||||
|
KeepAlive,
|
||||||
TrackOpTypes,
|
TrackOpTypes,
|
||||||
h,
|
h,
|
||||||
nextTick,
|
nextTick,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
|
onActivated,
|
||||||
onBeforeMount,
|
onBeforeMount,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
onBeforeUpdate,
|
onBeforeUpdate,
|
||||||
|
@ -407,4 +409,60 @@ describe('api: lifecycle hooks', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(fn).toHaveBeenCalledTimes(4)
|
expect(fn).toHaveBeenCalledTimes(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('immediately trigger unmount during rendering', async () => {
|
||||||
|
const fn = vi.fn()
|
||||||
|
const toggle = ref(false)
|
||||||
|
|
||||||
|
const Child = {
|
||||||
|
setup() {
|
||||||
|
onMounted(fn)
|
||||||
|
// trigger unmount immediately
|
||||||
|
toggle.value = false
|
||||||
|
return () => h('div')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Comp = {
|
||||||
|
setup() {
|
||||||
|
return () => (toggle.value ? [h(Child)] : null)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(h(Comp), nodeOps.createElement('div'))
|
||||||
|
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(fn).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('immediately trigger unmount during rendering(with KeepAlive)', async () => {
|
||||||
|
const mountedSpy = vi.fn()
|
||||||
|
const activeSpy = vi.fn()
|
||||||
|
const toggle = ref(false)
|
||||||
|
|
||||||
|
const Child = {
|
||||||
|
setup() {
|
||||||
|
onMounted(mountedSpy)
|
||||||
|
onActivated(activeSpy)
|
||||||
|
|
||||||
|
// trigger unmount immediately
|
||||||
|
toggle.value = false
|
||||||
|
return () => h('div')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Comp = {
|
||||||
|
setup() {
|
||||||
|
return () => h(KeepAlive, [toggle.value ? h(Child) : null])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
render(h(Comp), nodeOps.createElement('div'))
|
||||||
|
|
||||||
|
toggle.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(mountedSpy).toHaveBeenCalledTimes(0)
|
||||||
|
expect(activeSpy).toHaveBeenCalledTimes(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -31,9 +31,6 @@ export function injectHook(
|
||||||
const wrappedHook =
|
const wrappedHook =
|
||||||
hook.__weh ||
|
hook.__weh ||
|
||||||
(hook.__weh = (...args: unknown[]) => {
|
(hook.__weh = (...args: unknown[]) => {
|
||||||
if (target.isUnmounted) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// disable tracking inside all lifecycle hooks
|
// disable tracking inside all lifecycle hooks
|
||||||
// since they can potentially be called inside effects.
|
// since they can potentially be called inside effects.
|
||||||
pauseTracking()
|
pauseTracking()
|
||||||
|
|
|
@ -223,7 +223,7 @@ export type Component<
|
||||||
|
|
||||||
export type { ComponentOptions }
|
export type { ComponentOptions }
|
||||||
|
|
||||||
type LifecycleHook<TFn = Function> = TFn[] | null
|
export type LifecycleHook<TFn = Function> = (TFn & SchedulerJob)[] | null
|
||||||
|
|
||||||
// use `E extends any` to force evaluating type to fix #2362
|
// use `E extends any` to force evaluating type to fix #2362
|
||||||
export type SetupContext<
|
export type SetupContext<
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {
|
||||||
type RendererElement,
|
type RendererElement,
|
||||||
type RendererInternals,
|
type RendererInternals,
|
||||||
type RendererNode,
|
type RendererNode,
|
||||||
|
invalidateMount,
|
||||||
queuePostRenderEffect,
|
queuePostRenderEffect,
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { setTransitionHooks } from './BaseTransition'
|
import { setTransitionHooks } from './BaseTransition'
|
||||||
|
@ -166,6 +167,9 @@ const KeepAliveImpl: ComponentOptions = {
|
||||||
|
|
||||||
sharedContext.deactivate = (vnode: VNode) => {
|
sharedContext.deactivate = (vnode: VNode) => {
|
||||||
const instance = vnode.component!
|
const instance = vnode.component!
|
||||||
|
invalidateMount(instance.m)
|
||||||
|
invalidateMount(instance.a)
|
||||||
|
|
||||||
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
|
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
|
||||||
queuePostRenderEffect(() => {
|
queuePostRenderEffect(() => {
|
||||||
if (instance.da) {
|
if (instance.da) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
type ComponentInternalInstance,
|
type ComponentInternalInstance,
|
||||||
type ComponentOptions,
|
type ComponentOptions,
|
||||||
type Data,
|
type Data,
|
||||||
|
type LifecycleHook,
|
||||||
createComponentInstance,
|
createComponentInstance,
|
||||||
setupComponent,
|
setupComponent,
|
||||||
} from './component'
|
} from './component'
|
||||||
|
@ -2266,7 +2267,9 @@ function baseCreateRenderer(
|
||||||
unregisterHMR(instance)
|
unregisterHMR(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bum, scope, update, subTree, um } = instance
|
const { bum, scope, update, subTree, um, m, a } = instance
|
||||||
|
invalidateMount(m)
|
||||||
|
invalidateMount(a)
|
||||||
|
|
||||||
// beforeUnmount hook
|
// beforeUnmount hook
|
||||||
if (bum) {
|
if (bum) {
|
||||||
|
@ -2533,3 +2536,9 @@ function locateNonHydratedAsyncRoot(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invalidateMount(hooks: LifecycleHook) {
|
||||||
|
if (hooks) {
|
||||||
|
for (let i = 0; i < hooks.length; i++) hooks[i].active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -185,13 +185,11 @@ export function flushPostFlushCbs(seen?: CountMap) {
|
||||||
postFlushIndex < activePostFlushCbs.length;
|
postFlushIndex < activePostFlushCbs.length;
|
||||||
postFlushIndex++
|
postFlushIndex++
|
||||||
) {
|
) {
|
||||||
if (
|
const cb = activePostFlushCbs[postFlushIndex]
|
||||||
__DEV__ &&
|
if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {
|
||||||
checkRecursiveUpdates(seen!, activePostFlushCbs[postFlushIndex])
|
|
||||||
) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
activePostFlushCbs[postFlushIndex]()
|
if (cb.active !== false) cb()
|
||||||
}
|
}
|
||||||
activePostFlushCbs = null
|
activePostFlushCbs = null
|
||||||
postFlushIndex = 0
|
postFlushIndex = 0
|
||||||
|
|
Loading…
Reference in New Issue