Skip to content

Commit 2a633c8

Browse files
authored
fix(transition): should call transition hooks inside already resolved suspense (#1698)
fix #1689
1 parent 6efb2fe commit 2a633c8

File tree

2 files changed

+154
-2
lines changed

2 files changed

+154
-2
lines changed

packages/runtime-core/src/renderer.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -744,9 +744,12 @@ function baseCreateRenderer(
744744
}
745745

746746
hostInsert(el, container, anchor)
747-
// #1583 For inside suspense case, enter hook should call when suspense resolved
747+
// #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
748+
// #1689 For inside suspense + suspense resolved case, just call it
748749
const needCallTransitionHooks =
749-
!parentSuspense && transition && !transition.persisted
750+
(!parentSuspense || (parentSuspense && parentSuspense!.isResolved)) &&
751+
transition &&
752+
!transition.persisted
750753
if (
751754
(vnodeHook = props && props.onVnodeMounted) ||
752755
needCallTransitionHooks ||

packages/vue/__tests__/Transition.spec.ts

+149
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,155 @@ describe('e2e: Transition', () => {
11001100
)
11011101
})
11021102

1103+
describe('transition with Suspense', () => {
1104+
// #1583
1105+
test(
1106+
'async component transition inside Suspense',
1107+
async () => {
1108+
const onLeaveSpy = jest.fn()
1109+
const onEnterSpy = jest.fn()
1110+
1111+
await page().exposeFunction('onLeaveSpy', onLeaveSpy)
1112+
await page().exposeFunction('onEnterSpy', onEnterSpy)
1113+
1114+
await page().evaluate(() => {
1115+
const { onEnterSpy, onLeaveSpy } = window as any
1116+
const { createApp, ref, h } = (window as any).Vue
1117+
createApp({
1118+
template: `
1119+
<div id="container">
1120+
<Suspense>
1121+
<transition @enter="onEnterSpy"
1122+
@leave="onLeaveSpy">
1123+
<Comp v-if="toggle" class="test">content</Comp>
1124+
</transition>
1125+
</Suspense>
1126+
</div>
1127+
<button id="toggleBtn" @click="click">button</button>
1128+
`,
1129+
components: {
1130+
Comp: {
1131+
async setup() {
1132+
return () => h('div', { class: 'test' }, 'content')
1133+
}
1134+
}
1135+
},
1136+
setup: () => {
1137+
const toggle = ref(true)
1138+
const click = () => (toggle.value = !toggle.value)
1139+
return { toggle, click, onEnterSpy, onLeaveSpy }
1140+
}
1141+
}).mount('#app')
1142+
})
1143+
expect(await html('#container')).toBe('<div class="test">content</div>')
1144+
1145+
// leave
1146+
expect(await classWhenTransitionStart()).toStrictEqual([
1147+
'test',
1148+
'v-leave-active',
1149+
'v-leave-from'
1150+
])
1151+
expect(onLeaveSpy).toBeCalledTimes(1)
1152+
await nextFrame()
1153+
expect(await classList('.test')).toStrictEqual([
1154+
'test',
1155+
'v-leave-active',
1156+
'v-leave-to'
1157+
])
1158+
await transitionFinish()
1159+
expect(await html('#container')).toBe('<!--v-if-->')
1160+
1161+
// enter
1162+
const enterClass = await page().evaluate(async () => {
1163+
(document.querySelector('#toggleBtn') as any)!.click()
1164+
// nextTrick for patch start
1165+
await Promise.resolve()
1166+
// nextTrick for Suspense resolve
1167+
await Promise.resolve()
1168+
// nextTrick for dom transition start
1169+
await Promise.resolve()
1170+
return document
1171+
.querySelector('#container div')!
1172+
.className.split(/\s+/g)
1173+
})
1174+
expect(enterClass).toStrictEqual([
1175+
'test',
1176+
'v-enter-active',
1177+
'v-enter-from'
1178+
])
1179+
expect(onEnterSpy).toBeCalledTimes(1)
1180+
await nextFrame()
1181+
expect(await classList('.test')).toStrictEqual([
1182+
'test',
1183+
'v-enter-active',
1184+
'v-enter-to'
1185+
])
1186+
await transitionFinish()
1187+
expect(await html('#container')).toBe('<div class="test">content</div>')
1188+
},
1189+
E2E_TIMEOUT
1190+
)
1191+
1192+
// #1689
1193+
test(
1194+
'static node transition inside Suspense',
1195+
async () => {
1196+
await page().evaluate(() => {
1197+
const { createApp, ref } = (window as any).Vue
1198+
createApp({
1199+
template: `
1200+
<div id="container">
1201+
<Suspense>
1202+
<transition>
1203+
<div v-if="toggle" class="test">content</div>
1204+
</transition>
1205+
</Suspense>
1206+
</div>
1207+
<button id="toggleBtn" @click="click">button</button>
1208+
`,
1209+
setup: () => {
1210+
const toggle = ref(true)
1211+
const click = () => (toggle.value = !toggle.value)
1212+
return { toggle, click }
1213+
}
1214+
}).mount('#app')
1215+
})
1216+
expect(await html('#container')).toBe('<div class="test">content</div>')
1217+
1218+
// leave
1219+
expect(await classWhenTransitionStart()).toStrictEqual([
1220+
'test',
1221+
'v-leave-active',
1222+
'v-leave-from'
1223+
])
1224+
await nextFrame()
1225+
expect(await classList('.test')).toStrictEqual([
1226+
'test',
1227+
'v-leave-active',
1228+
'v-leave-to'
1229+
])
1230+
await transitionFinish()
1231+
expect(await html('#container')).toBe('<!--v-if-->')
1232+
1233+
// enter
1234+
expect(await classWhenTransitionStart()).toStrictEqual([
1235+
'test',
1236+
'v-enter-active',
1237+
'v-enter-from'
1238+
])
1239+
await nextFrame()
1240+
expect(await classList('.test')).toStrictEqual([
1241+
'test',
1242+
'v-enter-active',
1243+
'v-enter-to'
1244+
])
1245+
await transitionFinish()
1246+
expect(await html('#container')).toBe('<div class="test">content</div>')
1247+
},
1248+
E2E_TIMEOUT
1249+
)
1250+
})
1251+
11031252
describe('transition with v-show', () => {
11041253
test(
11051254
'named transition with v-show',

0 commit comments

Comments
 (0)