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
 | ||||
|           // #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