diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index db713a3f276..25ca1eaa40b 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -218,6 +218,75 @@ describe('hot module replacement', () => { expect(deactiveSpy).toHaveBeenCalledTimes(1) }) + // #7121 + test('reload KeepAlive slot in Transition', async () => { + const root = nodeOps.createElement('div') + const childId = 'test-transition-keep-alive-reload' + const unmountSpy = vi.fn() + const mountSpy = vi.fn() + const activeSpy = vi.fn() + const deactiveSpy = vi.fn() + + const Child: ComponentOptions = { + __hmrId: childId, + data() { + return { count: 0 } + }, + unmounted: unmountSpy, + render: compileToFunction(`
{{ count }}
`) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + components: { Child }, + data() { + return { toggle: true } + }, + render: compileToFunction( + `` + ) + } + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
0
`) + + reload(childId, { + __hmrId: childId, + data() { + return { count: 1 } + }, + mounted: mountSpy, + unmounted: unmountSpy, + activated: activeSpy, + deactivated: deactiveSpy, + render: compileToFunction(`
{{ count }}
`) + }) + await nextTick() + expect(serializeInner(root)).toBe(`
1
`) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(0) + + // should not unmount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(serializeInner(root)).toBe(``) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(1) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + + // should not mount when toggling + triggerEvent(root.children[1] as TestElement, 'click') + await nextTick() + expect(serializeInner(root)).toBe(`
1
`) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + expect(activeSpy).toHaveBeenCalledTimes(2) + expect(deactiveSpy).toHaveBeenCalledTimes(1) + }) + test('reload class component', async () => { const root = nodeOps.createElement('div') const childId = 'test4-child' diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 9cb80b94ef0..8711d7271c2 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -473,7 +473,11 @@ function emptyPlaceholder(vnode: VNode): VNode | undefined { function getKeepAliveChild(vnode: VNode): VNode | undefined { return isKeepAlive(vnode) - ? vnode.children + ? // #7121 ensure get the child component subtree in case + // it's been replaced during HMR + __DEV__ && vnode.component + ? vnode.component.subTree + : vnode.children ? ((vnode.children as VNodeArrayChildren)[0] as VNode) : undefined : vnode