diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 37534ad699f..6f6e40d5d48 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -20,7 +20,7 @@ import { PatchFlags, ShapeFlags, isArray, isFunction } from '@vue/shared' import { onBeforeUnmount, onMounted } from '../apiLifecycle' import { isTeleport } from './Teleport' import type { RendererElement } from '../renderer' -import { SchedulerJobFlags } from '../scheduler' +import { SchedulerJobFlags, queueJob } from '../scheduler' type Hook void> = T | T[] @@ -225,7 +225,7 @@ const BaseTransitionImpl: ComponentOptions = { // #6835 // it also needs to be updated when active is undefined if (!(instance.job.flags! & SchedulerJobFlags.DISPOSED)) { - instance.update() + queueJob(instance.update) } } return emptyPlaceholder(child) diff --git a/packages/vue/__tests__/e2e/Transition.spec.ts b/packages/vue/__tests__/e2e/Transition.spec.ts index b9e9117289e..21b98e533c6 100644 --- a/packages/vue/__tests__/e2e/Transition.spec.ts +++ b/packages/vue/__tests__/e2e/Transition.spec.ts @@ -1486,6 +1486,89 @@ describe('e2e: Transition', () => { ) }) + //#4933 + test( + '(out-in mode) transition on child components with empty root node', + async () => { + await page().evaluate(() => { + const { createApp, ref, computed } = (window as any).Vue + createApp({ + template: ` +
+ + + +
+ + `, + components: { + one: { + template: '
one
', + }, + two: { + template: '
two
', + }, + }, + setup: () => { + const toggle = ref(true) + const view = ref('two') + const key = computed(() => (view.value === 'one' ? 'one' : 'two')) + const change = () => + (view.value = view.value === 'one' ? 'two' : 'one') + return { toggle, change, view, key } + }, + }).mount('#app') + }) + expect(await html('#container')).toBe('
two
') + + // two -> one + expect(await classWhenTransitionStart()).toStrictEqual([ + 'test', + 'test-leave-from', + 'test-leave-active', + ]) + await nextFrame() + expect(await classList('.test')).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html('#container')).toBe('') + + // one -> two + expect(await classWhenTransitionStart()).toStrictEqual([ + 'test', + 'test-enter-from', + 'test-enter-active', + ]) + await nextFrame() + expect(await classList('.test')).toStrictEqual([ + 'test', + 'test-enter-active', + 'test-enter-to', + ]) + await transitionFinish() + expect(await html('#container')).toBe('
two
') + + // two -> one again + expect(await classWhenTransitionStart()).toStrictEqual([ + 'test', + 'test-leave-from', + 'test-leave-active', + ]) + await nextFrame() + expect(await classList('.test')).toStrictEqual([ + 'test', + 'test-leave-active', + 'test-leave-to', + ]) + await transitionFinish() + expect(await html('#container')).toBe('') + }, + E2E_TIMEOUT, + ) + describe('transition with Suspense', () => { // #1583 test(