fix(keep-alive): fix unmounting late-included components
fix #3648 based on #3650
This commit is contained in:
		
							parent
							
								
									20ed16f68c
								
							
						
					
					
						commit
						da49c863a2
					
				@ -18,7 +18,12 @@ import {
 | 
				
			|||||||
  defineAsyncComponent,
 | 
					  defineAsyncComponent,
 | 
				
			||||||
  Component,
 | 
					  Component,
 | 
				
			||||||
  createApp,
 | 
					  createApp,
 | 
				
			||||||
  onActivated
 | 
					  onActivated,
 | 
				
			||||||
 | 
					  onUnmounted,
 | 
				
			||||||
 | 
					  onMounted,
 | 
				
			||||||
 | 
					  reactive,
 | 
				
			||||||
 | 
					  shallowRef,
 | 
				
			||||||
 | 
					  onDeactivated
 | 
				
			||||||
} from '@vue/runtime-test'
 | 
					} from '@vue/runtime-test'
 | 
				
			||||||
import { KeepAliveProps } from '../../src/components/KeepAlive'
 | 
					import { KeepAliveProps } from '../../src/components/KeepAlive'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -903,4 +908,73 @@ describe('KeepAlive', () => {
 | 
				
			|||||||
    await nextTick()
 | 
					    await nextTick()
 | 
				
			||||||
    expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
 | 
					    expect(handler).toHaveBeenCalledWith(err, {}, 'activated hook')
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // #3648
 | 
				
			||||||
 | 
					  test('should avoid unmount later included components', async () => {
 | 
				
			||||||
 | 
					    const unmountedA = jest.fn()
 | 
				
			||||||
 | 
					    const mountedA = jest.fn()
 | 
				
			||||||
 | 
					    const activatedA = jest.fn()
 | 
				
			||||||
 | 
					    const deactivatedA = jest.fn()
 | 
				
			||||||
 | 
					    const unmountedB = jest.fn()
 | 
				
			||||||
 | 
					    const mountedB = jest.fn()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const A = {
 | 
				
			||||||
 | 
					      name: 'A',
 | 
				
			||||||
 | 
					      setup() {
 | 
				
			||||||
 | 
					        onMounted(mountedA)
 | 
				
			||||||
 | 
					        onUnmounted(unmountedA)
 | 
				
			||||||
 | 
					        onActivated(activatedA)
 | 
				
			||||||
 | 
					        onDeactivated(deactivatedA)
 | 
				
			||||||
 | 
					        return () => 'A'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const B = {
 | 
				
			||||||
 | 
					      name: 'B',
 | 
				
			||||||
 | 
					      setup() {
 | 
				
			||||||
 | 
					        onMounted(mountedB)
 | 
				
			||||||
 | 
					        onUnmounted(unmountedB)
 | 
				
			||||||
 | 
					        return () => 'B'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const include = reactive<string[]>([])
 | 
				
			||||||
 | 
					    const current = shallowRef(A)
 | 
				
			||||||
 | 
					    const app = createApp({
 | 
				
			||||||
 | 
					      setup() {
 | 
				
			||||||
 | 
					        return () => {
 | 
				
			||||||
 | 
					          return [
 | 
				
			||||||
 | 
					            h(
 | 
				
			||||||
 | 
					              KeepAlive,
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                include
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              h(current.value)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.mount(root)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(serializeInner(root)).toBe(`A`)
 | 
				
			||||||
 | 
					    expect(mountedA).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					    expect(unmountedA).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(activatedA).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(deactivatedA).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(mountedB).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(unmountedB).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    include.push('A') // cache A
 | 
				
			||||||
 | 
					    await nextTick()
 | 
				
			||||||
 | 
					    current.value = B // toggle to B
 | 
				
			||||||
 | 
					    await nextTick()
 | 
				
			||||||
 | 
					    expect(serializeInner(root)).toBe(`B`)
 | 
				
			||||||
 | 
					    expect(mountedA).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					    expect(unmountedA).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(activatedA).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					    expect(deactivatedA).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					    expect(mountedB).toHaveBeenCalledTimes(1)
 | 
				
			||||||
 | 
					    expect(unmountedB).toHaveBeenCalledTimes(0)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,7 @@ import { setTransitionHooks } from './BaseTransition'
 | 
				
			|||||||
import { ComponentRenderContext } from '../componentPublicInstance'
 | 
					import { ComponentRenderContext } from '../componentPublicInstance'
 | 
				
			||||||
import { devtoolsComponentAdded } from '../devtools'
 | 
					import { devtoolsComponentAdded } from '../devtools'
 | 
				
			||||||
import { isAsyncWrapper } from '../apiAsyncComponent'
 | 
					import { isAsyncWrapper } from '../apiAsyncComponent'
 | 
				
			||||||
 | 
					import { isSuspense } from './Suspense'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MatchPattern = string | RegExp | (string | RegExp)[]
 | 
					type MatchPattern = string | RegExp | (string | RegExp)[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -323,7 +324,7 @@ const KeepAliveImpl: ComponentOptions = {
 | 
				
			|||||||
      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
 | 
					      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      current = vnode
 | 
					      current = vnode
 | 
				
			||||||
      return rawVNode
 | 
					      return isSuspense(rawVNode.type) ? rawVNode : vnode
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user