diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts
index eaef8d401a7..060d3ec9a50 100644
--- a/packages/runtime-core/__tests__/hmr.spec.ts
+++ b/packages/runtime-core/__tests__/hmr.spec.ts
@@ -469,4 +469,61 @@ describe('hot module replacement', () => {
render(h(Foo), root)
expect(serializeInner(root)).toBe('bar')
})
+
+ test('reload for keep-alive instance',async () => {
+ const parentId = 'keep-alive-parent-reload'
+ const barId = 'keep-alive-bar-reload'
+ const fooId = 'keep-alive-foo-reload'
+ const root = nodeOps.createElement('div')
+ const Bar: ComponentOptions = {
+ __hmrId: barId,
+ render: compileToFunction(`
bar
`)
+ }
+ const Foo: ComponentOptions = {
+ __hmrId: fooId,
+ render: compileToFunction(`foo
`)
+ }
+ const Parent: ComponentOptions = {
+ __hmrId: parentId,
+ data() {
+ return {
+ value: 'bar'
+ }
+ },
+ components: {
+ foo: Foo,
+ bar: Bar
+ },
+ render: compileToFunction(`
+
+
+
+
+ `)
+ }
+ createRecord(parentId, Parent)
+ createRecord(barId, Bar)
+ createRecord(fooId, Foo)
+ const app = createApp(Parent)
+ app.mount(root)
+ expect(serializeInner(root)).toBe('bar
')
+ reload(barId, {
+ __hmrId: barId,
+ render: compileToFunction(`bar1
`)
+ })
+ await nextTick()
+ expect(serializeInner(root)).toBe('bar1
')
+ triggerEvent(root.children[1] as TestElement, 'click')
+ await nextTick()
+ expect(serializeInner(root)).toBe('foo
')
+ triggerEvent(root.children[1] as TestElement, 'click')
+ await nextTick()
+ expect(serializeInner(root)).toBe('bar1
')
+ reload(barId, {
+ __hmrId: barId,
+ render: compileToFunction(`bar2
`)
+ })
+ await nextTick()
+ expect(serializeInner(root)).toBe('bar2
')
+ })
})
diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts
index c6180019f86..5970c5f6e87 100644
--- a/packages/runtime-core/src/components/KeepAlive.ts
+++ b/packages/runtime-core/src/components/KeepAlive.ts
@@ -42,6 +42,7 @@ import { setTransitionHooks } from './BaseTransition'
import { ComponentRenderContext } from '../componentPublicInstance'
import { devtoolsComponentAdded } from '../devtools'
import { isAsyncWrapper } from '../apiAsyncComponent'
+import { registerHMR, unregisterHMR } from '../hmr'
type MatchPattern = string | RegExp | string[] | RegExp[]
@@ -121,6 +122,9 @@ const KeepAliveImpl: ComponentOptions = {
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
const instance = vnode.component!
+ if(__DEV__ && instance.type.__hmrId) {
+ registerHMR(instance);
+ }
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
// in case props have changed
patch(
@@ -153,6 +157,9 @@ const KeepAliveImpl: ComponentOptions = {
sharedContext.deactivate = (vnode: VNode) => {
const instance = vnode.component!
+ if(__DEV__ && instance.type.__hmrId) {
+ unregisterHMR(instance);
+ }
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
queuePostRenderEffect(() => {
if (instance.da) {
@@ -171,6 +178,8 @@ const KeepAliveImpl: ComponentOptions = {
}
}
+ sharedContext.pruneCacheEntry = pruneCacheEntry
+
function unmount(vnode: VNode) {
// reset the shapeFlag so it can be properly unmounted
resetShapeFlag(vnode)
diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts
index 3c3f5208bcc..1bc7c919015 100644
--- a/packages/runtime-core/src/hmr.ts
+++ b/packages/runtime-core/src/hmr.ts
@@ -112,6 +112,13 @@ function reload(id: string, newComp: HMRComponent) {
for (const instance of instances) {
const oldComp = normalizeClassComponent(instance.type as HMRComponent)
+ //#5073 # need to cleanup the cache in keep-alive and reset the shapeFlag
+ if(instance.parent && (instance.parent.type as ComponentOptions).__isKeepAlive) {
+ const vnode = instance.vnode
+ const key = vnode.key == null ? vnode.type : vnode.key;
+ (instance.parent.ctx as any).pruneCacheEntry(key)
+ }
+
if (!hmrDirtyComponents.has(oldComp)) {
// 1. Update existing comp definition to match new one
if (oldComp !== record.initialDef) {