fix(hmr): avoid infinite recursion when reloading hmr components (#6936)

close #6930
This commit is contained in:
Zeke Zhang 2024-05-31 23:41:13 +08:00 committed by GitHub
parent f1cc478819
commit 36bd9b0a1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import {
h,
nextTick,
nodeOps,
ref,
render,
serializeInner,
triggerEvent,
@ -415,6 +416,53 @@ describe('hot module replacement', () => {
expect(mountSpy).toHaveBeenCalledTimes(1)
})
// #6930
test('reload: avoid infinite recursion', async () => {
const root = nodeOps.createElement('div')
const childId = 'test-child-6930'
const unmountSpy = vi.fn()
const mountSpy = vi.fn()
const Child: ComponentOptions = {
__hmrId: childId,
data() {
return { count: 0 }
},
expose: ['count'],
unmounted: unmountSpy,
render: compileToFunction(`<div @click="count++">{{ count }}</div>`),
}
createRecord(childId, Child)
const Parent: ComponentOptions = {
setup() {
const com = ref()
const changeRef = (value: any) => {
com.value = value
}
return () => [h(Child, { ref: changeRef }), com.value?.count]
},
}
render(h(Parent), root)
await nextTick()
expect(serializeInner(root)).toBe(`<div>0</div>0`)
reload(childId, {
__hmrId: childId,
data() {
return { count: 1 }
},
mounted: mountSpy,
render: compileToFunction(`<div @click="count++">{{ count }}</div>`),
})
await nextTick()
expect(serializeInner(root)).toBe(`<div>1</div>1`)
expect(unmountSpy).toHaveBeenCalledTimes(1)
expect(mountSpy).toHaveBeenCalledTimes(1)
})
// #1156 - static nodes should retain DOM element reference across updates
// when HMR is active
test('static el reference', async () => {

View File

@ -139,7 +139,11 @@ function reload(id: string, newComp: HMRComponent) {
// components to be unmounted and re-mounted. Queue the update so that we
// don't end up forcing the same parent to re-render multiple times.
instance.parent.effect.dirty = true
queueJob(instance.parent.update)
queueJob(() => {
instance.parent!.update()
// #6930 avoid infinite recursion
hmrDirtyComponents.delete(oldComp)
})
} else if (instance.appContext.reload) {
// root instance mounted via createApp() has a reload method
instance.appContext.reload()