Skip to content

Commit 1f080ec

Browse files
committed
fix(hmr/transition): fix transition elements disappearing after reloading component (#7121)
1 parent 01f43c1 commit 1f080ec

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

packages/runtime-core/__tests__/hmr.spec.ts

+69
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,75 @@ describe('hot module replacement', () => {
219219
expect(deactiveSpy).toHaveBeenCalledTimes(1)
220220
})
221221

222+
// #7121
223+
test('reload KeepAlive slot in Transition', async () => {
224+
const root = nodeOps.createElement('div')
225+
const childId = 'test-transition-keep-alive-reload'
226+
const unmountSpy = jest.fn()
227+
const mountSpy = jest.fn()
228+
const activeSpy = jest.fn()
229+
const deactiveSpy = jest.fn()
230+
231+
const Child: ComponentOptions = {
232+
__hmrId: childId,
233+
data() {
234+
return { count: 0 }
235+
},
236+
unmounted: unmountSpy,
237+
render: compileToFunction(`<div>{{ count }}</div>`)
238+
}
239+
createRecord(childId, Child)
240+
241+
const Parent: ComponentOptions = {
242+
components: { Child },
243+
data() {
244+
return { toggle: true }
245+
},
246+
render: compileToFunction(
247+
`<button @click="toggle = !toggle"></button><BaseTransition mode="out-in"><KeepAlive><Child v-if="toggle" /></KeepAlive></BaseTransition>`
248+
)
249+
}
250+
251+
render(h(Parent), root)
252+
expect(serializeInner(root)).toBe(`<button></button><div>0</div>`)
253+
254+
reload(childId, {
255+
__hmrId: childId,
256+
data() {
257+
return { count: 1 }
258+
},
259+
mounted: mountSpy,
260+
unmounted: unmountSpy,
261+
activated: activeSpy,
262+
deactivated: deactiveSpy,
263+
render: compileToFunction(`<div>{{ count }}</div>`)
264+
})
265+
await nextTick()
266+
expect(serializeInner(root)).toBe(`<button></button><div>1</div>`)
267+
expect(unmountSpy).toHaveBeenCalledTimes(1)
268+
expect(mountSpy).toHaveBeenCalledTimes(1)
269+
expect(activeSpy).toHaveBeenCalledTimes(1)
270+
expect(deactiveSpy).toHaveBeenCalledTimes(0)
271+
272+
// should not unmount when toggling
273+
triggerEvent(root.children[1] as TestElement, 'click')
274+
await nextTick()
275+
expect(serializeInner(root)).toBe(`<button></button><!---->`)
276+
expect(unmountSpy).toHaveBeenCalledTimes(1)
277+
expect(mountSpy).toHaveBeenCalledTimes(1)
278+
expect(activeSpy).toHaveBeenCalledTimes(1)
279+
expect(deactiveSpy).toHaveBeenCalledTimes(1)
280+
281+
// should not mount when toggling
282+
triggerEvent(root.children[1] as TestElement, 'click')
283+
await nextTick()
284+
expect(serializeInner(root)).toBe(`<button></button><div>1</div>`)
285+
expect(unmountSpy).toHaveBeenCalledTimes(1)
286+
expect(mountSpy).toHaveBeenCalledTimes(1)
287+
expect(activeSpy).toHaveBeenCalledTimes(2)
288+
expect(deactiveSpy).toHaveBeenCalledTimes(1)
289+
})
290+
222291
test('reload class component', async () => {
223292
const root = nodeOps.createElement('div')
224293
const childId = 'test4-child'

packages/runtime-core/src/components/BaseTransition.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,10 @@ function emptyPlaceholder(vnode: VNode): VNode | undefined {
467467

468468
function getKeepAliveChild(vnode: VNode): VNode | undefined {
469469
return isKeepAlive(vnode)
470-
? vnode.children
470+
? // #7121 avoid the result is expired during HMR
471+
vnode.component
472+
? vnode.component.subTree
473+
: vnode.children
471474
? ((vnode.children as VNodeArrayChildren)[0] as VNode)
472475
: undefined
473476
: vnode

0 commit comments

Comments
 (0)