import {
+ KeepAlive,
TrackOpTypes,
h,
nextTick,
nodeOps,
+ onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
await nextTick()
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)
+ })
})
const wrappedHook =
hook.__weh ||
(hook.__weh = (...args: unknown[]) => {
- if (target.isUnmounted) {
- return
- }
// disable tracking inside all lifecycle hooks
// since they can potentially be called inside effects.
pauseTracking()
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
export type SetupContext<
type RendererElement,
type RendererInternals,
type RendererNode,
+ invalidateMount,
queuePostRenderEffect,
} from '../renderer'
import { setTransitionHooks } from './BaseTransition'
sharedContext.deactivate = (vnode: VNode) => {
const instance = vnode.component!
+ invalidateMount(instance.m)
+ invalidateMount(instance.a)
+
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
queuePostRenderEffect(() => {
if (instance.da) {
type ComponentInternalInstance,
type ComponentOptions,
type Data,
+ type LifecycleHook,
createComponentInstance,
setupComponent,
} from './component'
unregisterHMR(instance)
}
- const { bum, scope, update, subTree, um } = instance
+ const { bum, scope, update, subTree, um, m, a } = instance
+ invalidateMount(m)
+ invalidateMount(a)
// beforeUnmount hook
if (bum) {
}
}
}
+
+export function invalidateMount(hooks: LifecycleHook) {
+ if (hooks) {
+ for (let i = 0; i < hooks.length; i++) hooks[i].active = false
+ }
+}
postFlushIndex < activePostFlushCbs.length;
postFlushIndex++
) {
- if (
- __DEV__ &&
- checkRecursiveUpdates(seen!, activePostFlushCbs[postFlushIndex])
- ) {
+ const cb = activePostFlushCbs[postFlushIndex]
+ if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {
continue
}
- activePostFlushCbs[postFlushIndex]()
+ if (cb.active !== false) cb()
}
activePostFlushCbs = null
postFlushIndex = 0