Skip to content

Commit 9d30aff

Browse files
authored
fix(transition): handle transition on pre-resolved async components (vuejs#14314)
1 parent a7d8adc commit 9d30aff

File tree

4 files changed

+85
-10
lines changed

4 files changed

+85
-10
lines changed

packages-private/vapor-e2e-test/__tests__/transition.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,59 @@ describe('vapor transition', () => {
11791179
'<div class="">vapor compA</div>',
11801180
)
11811181
})
1182+
1183+
test('apply transition to pre-resolved async component', async () => {
1184+
const btnSelector = '.async-resolved > button'
1185+
const containerSelector = '.async-resolved #container'
1186+
const hiddenCompSelector = '.async-resolved #hidden-async'
1187+
1188+
// Wait for the hidden AsyncCompResolved to resolve and render
1189+
await waitForInnerHTML(
1190+
hiddenCompSelector,
1191+
'<div style="display: none;">vapor compA</div>',
1192+
)
1193+
1194+
expect(await html(containerSelector)).toBe('')
1195+
1196+
await click(btnSelector)
1197+
expect(await html(containerSelector)).toBe(
1198+
'<div class="v-enter-from v-enter-active">vapor compA</div>',
1199+
)
1200+
await waitForInnerHTML(
1201+
containerSelector,
1202+
'<div class="v-enter-active v-enter-to">vapor compA</div>',
1203+
)
1204+
await waitForInnerHTML(
1205+
containerSelector,
1206+
'<div class="">vapor compA</div>',
1207+
)
1208+
1209+
// leave
1210+
await click(btnSelector)
1211+
await nextTick()
1212+
expect(await html(containerSelector)).toBe(
1213+
'<div class="v-leave-from v-leave-active">vapor compA</div>',
1214+
)
1215+
await waitForInnerHTML(
1216+
containerSelector,
1217+
'<div class="v-leave-active v-leave-to">vapor compA</div>',
1218+
)
1219+
await waitForInnerHTML(containerSelector, '')
1220+
1221+
// enter again
1222+
await click(btnSelector)
1223+
expect(await html(containerSelector)).toBe(
1224+
'<div class="v-enter-from v-enter-active">vapor compA</div>',
1225+
)
1226+
await waitForInnerHTML(
1227+
containerSelector,
1228+
'<div class="v-enter-active v-enter-to">vapor compA</div>',
1229+
)
1230+
await waitForInnerHTML(
1231+
containerSelector,
1232+
'<div class="">vapor compA</div>',
1233+
)
1234+
})
11821235
})
11831236

11841237
describe('transition with v-show', () => {

packages-private/vapor-e2e-test/transition/App.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ const AsyncComp = defineVaporAsyncComponent(() => {
112112
return new Promise(resolve => setTimeout(() => resolve(VaporCompA), 50))
113113
})
114114
115+
const AsyncCompResolved = defineVaporAsyncComponent(() =>
116+
Promise.resolve(VaporCompA),
117+
)
118+
115119
const TrueBranch = defineVaporComponent({
116120
name: 'TrueBranch',
117121
setup() {
@@ -649,6 +653,18 @@ const Comp2 = defineVaporComponent({
649653
</div>
650654
<button @click="toggle = !toggle">button</button>
651655
</div>
656+
<div class="async-resolved">
657+
<!-- Pre-resolve the async component by rendering it hidden -->
658+
<div id="hidden-async">
659+
<AsyncCompResolved v-show="false" />
660+
</div>
661+
<div id="container">
662+
<transition>
663+
<AsyncCompResolved v-if="!toggle"></AsyncCompResolved>
664+
</transition>
665+
</div>
666+
<button @click="toggle = !toggle">button</button>
667+
</div>
652668
<!-- async component end -->
653669

654670
<!-- with teleport -->

packages/runtime-vapor/src/apiDefineAsyncComponent.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
import { invokeArrayFns } from '@vue/shared'
2929
import { type TransitionOptions, insert, remove } from './block'
3030
import { parentNode } from './dom/node'
31-
import { setTransitionHooks } from './components/Transition'
3231

3332
/*@ __NO_SIDE_EFFECTS__ */
3433
export function defineVaporAsyncComponent<T extends VaporComponent>(
@@ -167,7 +166,6 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
167166
render = () => createComponent(loadingComponent)
168167
}
169168

170-
if (instance.$transition) frag!.$transition = instance.$transition
171169
frag.update(render)
172170
// Manually trigger cacheBlock for KeepAlive
173171
if (frag.keepAliveCtx) frag.keepAliveCtx.cacheBlock()
@@ -183,7 +181,7 @@ function createInnerComp(
183181
parent: VaporComponentInstance & TransitionOptions,
184182
frag?: DynamicFragment,
185183
): VaporComponentInstance {
186-
const { rawProps, rawSlots, appContext, $transition } = parent
184+
const { rawProps, rawSlots, appContext } = parent
187185
const instance = createComponent(
188186
comp,
189187
rawProps,
@@ -195,9 +193,6 @@ function createInnerComp(
195193
appContext,
196194
)
197195

198-
// set transition hooks
199-
if ($transition) setTransitionHooks(instance, $transition)
200-
201196
// set ref
202197
frag && frag.setAsyncRef && frag.setAsyncRef(instance)
203198

packages/runtime-vapor/src/components/Transition.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ import {
3232
} from '../component'
3333
import { isArray } from '@vue/shared'
3434
import { renderEffect } from '../renderEffect'
35-
import { type VaporFragment, isFragment } from '../fragment'
35+
import {
36+
type DynamicFragment,
37+
type VaporFragment,
38+
isFragment,
39+
} from '../fragment'
3640
import {
3741
currentHydrationNode,
3842
isHydrating,
@@ -288,9 +292,16 @@ export function findTransitionBlock(
288292
// transition can only be applied on Element child
289293
if (block instanceof Element) child = block
290294
} else if (isVaporComponent(block)) {
291-
// should save hooks on unresolved async wrapper, so that it can be applied after resolved
292-
if (isAsyncWrapper(block) && !block.type.__asyncResolved) {
293-
child = block
295+
if (isAsyncWrapper(block)) {
296+
// for unresolved async wrapper, set transition hooks on inner fragment
297+
if (!block.type.__asyncResolved) {
298+
onFragment && onFragment(block.block! as DynamicFragment)
299+
} else {
300+
child = findTransitionBlock(
301+
(block.block! as DynamicFragment).nodes,
302+
onFragment,
303+
)
304+
}
294305
} else {
295306
// stop searching if encountering nested Transition component
296307
if (getComponentName(block.type) === displayName) return undefined

0 commit comments

Comments
 (0)