mirror of https://github.com/vuejs/core.git
fix(suspense): fix anchor for suspense with transition out-in (#9999)
close #9996
This commit is contained in:
parent
7220c58d99
commit
a3fbf2132b
|
@ -400,7 +400,6 @@ export interface SuspenseBoundary {
|
|||
namespace: ElementNamespace
|
||||
container: RendererElement
|
||||
hiddenContainer: RendererElement
|
||||
anchor: RendererNode | null
|
||||
activeBranch: VNode | null
|
||||
pendingBranch: VNode | null
|
||||
deps: number
|
||||
|
@ -473,6 +472,7 @@ function createSuspenseBoundary(
|
|||
assertNumber(timeout, `Suspense timeout`)
|
||||
}
|
||||
|
||||
const initialAnchor = anchor
|
||||
const suspense: SuspenseBoundary = {
|
||||
vnode,
|
||||
parent: parentSuspense,
|
||||
|
@ -480,7 +480,6 @@ function createSuspenseBoundary(
|
|||
namespace,
|
||||
container,
|
||||
hiddenContainer,
|
||||
anchor,
|
||||
deps: 0,
|
||||
pendingId: suspenseId++,
|
||||
timeout: typeof timeout === 'number' ? timeout : -1,
|
||||
|
@ -529,20 +528,28 @@ function createSuspenseBoundary(
|
|||
move(
|
||||
pendingBranch!,
|
||||
container,
|
||||
next(activeBranch!),
|
||||
anchor === initialAnchor ? next(activeBranch!) : anchor,
|
||||
MoveType.ENTER,
|
||||
)
|
||||
queuePostFlushCb(effects)
|
||||
}
|
||||
}
|
||||
}
|
||||
// this is initial anchor on mount
|
||||
let { anchor } = suspense
|
||||
// unmount current active tree
|
||||
if (activeBranch) {
|
||||
// if the fallback tree was mounted, it may have been moved
|
||||
// as part of a parent suspense. get the latest anchor for insertion
|
||||
anchor = next(activeBranch)
|
||||
// #8105 if `delayEnter` is true, it means that the mounting of
|
||||
// `activeBranch` will be delayed. if the branch switches before
|
||||
// transition completes, both `activeBranch` and `pendingBranch` may
|
||||
// coexist in the `hiddenContainer`. This could result in
|
||||
// `next(activeBranch!)` obtaining an incorrect anchor
|
||||
// (got `pendingBranch.el`).
|
||||
// Therefore, after the mounting of activeBranch is completed,
|
||||
// it is necessary to get the latest anchor.
|
||||
if (parentNode(activeBranch.el!) !== suspense.hiddenContainer) {
|
||||
anchor = next(activeBranch)
|
||||
}
|
||||
unmount(activeBranch, parentComponent, suspense, true)
|
||||
}
|
||||
if (!delayEnter) {
|
||||
|
|
|
@ -1652,6 +1652,77 @@ describe('e2e: Transition', () => {
|
|||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
|
||||
// #9996
|
||||
test(
|
||||
'trigger again when transition is not finished & correctly anchor',
|
||||
async () => {
|
||||
await page().evaluate(duration => {
|
||||
const { createApp, shallowRef, h } = (window as any).Vue
|
||||
const One = {
|
||||
async setup() {
|
||||
return () => h('div', { class: 'test' }, 'one')
|
||||
},
|
||||
}
|
||||
const Two = {
|
||||
async setup() {
|
||||
return () => h('div', { class: 'test' }, 'two')
|
||||
},
|
||||
}
|
||||
createApp({
|
||||
template: `
|
||||
<div id="container">
|
||||
<div>Top</div>
|
||||
<transition name="test" mode="out-in" :duration="${duration}">
|
||||
<Suspense>
|
||||
<component :is="view"/>
|
||||
</Suspense>
|
||||
</transition>
|
||||
<div>Bottom</div>
|
||||
</div>
|
||||
<button id="toggleBtn" @click="click">button</button>
|
||||
`,
|
||||
setup: () => {
|
||||
const view = shallowRef(One)
|
||||
const click = () => {
|
||||
view.value = view.value === One ? Two : One
|
||||
}
|
||||
return { view, click }
|
||||
},
|
||||
}).mount('#app')
|
||||
}, duration)
|
||||
|
||||
await nextFrame()
|
||||
expect(await html('#container')).toBe(
|
||||
'<div>Top</div><div class="test test-enter-active test-enter-to">one</div><div>Bottom</div>',
|
||||
)
|
||||
|
||||
await transitionFinish()
|
||||
expect(await html('#container')).toBe(
|
||||
'<div>Top</div><div class="test">one</div><div>Bottom</div>',
|
||||
)
|
||||
|
||||
// trigger twice
|
||||
classWhenTransitionStart()
|
||||
await nextFrame()
|
||||
expect(await html('#container')).toBe(
|
||||
'<div>Top</div><div class="test test-leave-active test-leave-to">one</div><div>Bottom</div>',
|
||||
)
|
||||
|
||||
await transitionFinish()
|
||||
await nextFrame()
|
||||
expect(await html('#container')).toBe(
|
||||
'<div>Top</div><div class="test test-enter-active test-enter-to">two</div><div>Bottom</div>',
|
||||
)
|
||||
|
||||
await transitionFinish()
|
||||
await nextFrame()
|
||||
expect(await html('#container')).toBe(
|
||||
'<div>Top</div><div class="test">two</div><div>Bottom</div>',
|
||||
)
|
||||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
})
|
||||
|
||||
describe('transition with v-show', () => {
|
||||
|
|
Loading…
Reference in New Issue