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
|
namespace: ElementNamespace
|
||||||
container: RendererElement
|
container: RendererElement
|
||||||
hiddenContainer: RendererElement
|
hiddenContainer: RendererElement
|
||||||
anchor: RendererNode | null
|
|
||||||
activeBranch: VNode | null
|
activeBranch: VNode | null
|
||||||
pendingBranch: VNode | null
|
pendingBranch: VNode | null
|
||||||
deps: number
|
deps: number
|
||||||
|
@ -473,6 +472,7 @@ function createSuspenseBoundary(
|
||||||
assertNumber(timeout, `Suspense timeout`)
|
assertNumber(timeout, `Suspense timeout`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialAnchor = anchor
|
||||||
const suspense: SuspenseBoundary = {
|
const suspense: SuspenseBoundary = {
|
||||||
vnode,
|
vnode,
|
||||||
parent: parentSuspense,
|
parent: parentSuspense,
|
||||||
|
@ -480,7 +480,6 @@ function createSuspenseBoundary(
|
||||||
namespace,
|
namespace,
|
||||||
container,
|
container,
|
||||||
hiddenContainer,
|
hiddenContainer,
|
||||||
anchor,
|
|
||||||
deps: 0,
|
deps: 0,
|
||||||
pendingId: suspenseId++,
|
pendingId: suspenseId++,
|
||||||
timeout: typeof timeout === 'number' ? timeout : -1,
|
timeout: typeof timeout === 'number' ? timeout : -1,
|
||||||
|
@ -529,20 +528,28 @@ function createSuspenseBoundary(
|
||||||
move(
|
move(
|
||||||
pendingBranch!,
|
pendingBranch!,
|
||||||
container,
|
container,
|
||||||
next(activeBranch!),
|
anchor === initialAnchor ? next(activeBranch!) : anchor,
|
||||||
MoveType.ENTER,
|
MoveType.ENTER,
|
||||||
)
|
)
|
||||||
queuePostFlushCb(effects)
|
queuePostFlushCb(effects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// this is initial anchor on mount
|
|
||||||
let { anchor } = suspense
|
|
||||||
// unmount current active tree
|
// unmount current active tree
|
||||||
if (activeBranch) {
|
if (activeBranch) {
|
||||||
// if the fallback tree was mounted, it may have been moved
|
// if the fallback tree was mounted, it may have been moved
|
||||||
// as part of a parent suspense. get the latest anchor for insertion
|
// as part of a parent suspense. get the latest anchor for insertion
|
||||||
|
// #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)
|
anchor = next(activeBranch)
|
||||||
|
}
|
||||||
unmount(activeBranch, parentComponent, suspense, true)
|
unmount(activeBranch, parentComponent, suspense, true)
|
||||||
}
|
}
|
||||||
if (!delayEnter) {
|
if (!delayEnter) {
|
||||||
|
|
|
@ -1652,6 +1652,77 @@ describe('e2e: Transition', () => {
|
||||||
},
|
},
|
||||||
E2E_TIMEOUT,
|
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', () => {
|
describe('transition with v-show', () => {
|
||||||
|
|
Loading…
Reference in New Issue