Skip to content

Commit ef20b86

Browse files
authored
fix(hmr): prevent update unmounting component during HMR reload (#13815)
close vitejs/vite-plugin-vue#599
1 parent 35da3c6 commit ef20b86

File tree

2 files changed

+60
-5
lines changed

2 files changed

+60
-5
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,4 +937,55 @@ describe('hot module replacement', () => {
937937
rerender(id, () => 'bar')
938938
expect(serializeInner(root)).toBe('bar')
939939
})
940+
941+
// https://github.com/vitejs/vite-plugin-vue/issues/599
942+
// Both Outer and Inner are reloaded when './server.js' changes
943+
test('reload nested components from single update', async () => {
944+
const innerId = 'nested-reload-inner'
945+
const outerId = 'nested-reload-outer'
946+
947+
let Inner = {
948+
__hmrId: innerId,
949+
render() {
950+
return h('div', 'foo')
951+
},
952+
}
953+
let Outer = {
954+
__hmrId: outerId,
955+
render() {
956+
return h(Inner)
957+
},
958+
}
959+
960+
createRecord(innerId, Inner)
961+
createRecord(outerId, Outer)
962+
963+
const App = {
964+
render: () => h(Outer),
965+
}
966+
967+
const root = nodeOps.createElement('div')
968+
render(h(App), root)
969+
expect(serializeInner(root)).toBe('<div>foo</div>')
970+
971+
Inner = {
972+
__hmrId: innerId,
973+
render() {
974+
return h('div', 'bar')
975+
},
976+
}
977+
Outer = {
978+
__hmrId: outerId,
979+
render() {
980+
return h(Inner)
981+
},
982+
}
983+
984+
// trigger reload for both Outer and Inner
985+
reload(outerId, Outer)
986+
reload(innerId, Inner)
987+
await nextTick()
988+
989+
expect(serializeInner(root)).toBe('<div>bar</div>')
990+
})
940991
})

packages/runtime-core/src/hmr.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,15 @@ function reload(id: string, newComp: HMRComponent): void {
147147
// components to be unmounted and re-mounted. Queue the update so that we
148148
// don't end up forcing the same parent to re-render multiple times.
149149
queueJob(() => {
150-
isHmrUpdating = true
151-
instance.parent!.update()
152-
isHmrUpdating = false
153-
// #6930, #11248 avoid infinite recursion
154-
dirtyInstances.delete(instance)
150+
// vite-plugin-vue/issues/599
151+
// don't update if the job is already disposed
152+
if (!(instance.job.flags! & SchedulerJobFlags.DISPOSED)) {
153+
isHmrUpdating = true
154+
instance.parent!.update()
155+
isHmrUpdating = false
156+
// #6930, #11248 avoid infinite recursion
157+
dirtyInstances.delete(instance)
158+
}
155159
})
156160
} else if (instance.appContext.reload) {
157161
// root instance mounted via createApp() has a reload method

0 commit comments

Comments
 (0)