Skip to content

Commit

Permalink
fix(hmr): avoid infinite recursion when reloading hmr components (vue…
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangzhonghe committed Apr 24, 2023
1 parent 01f43c1 commit 26a6554
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
50 changes: 49 additions & 1 deletion packages/runtime-core/__tests__/hmr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
serializeInner,
triggerEvent,
TestElement,
nextTick
nextTick,
ref
} from '@vue/runtime-test'
import * as runtimeTest from '@vue/runtime-test'
import { registerRuntimeCompiler, createApp } from '@vue/runtime-test'
Expand Down Expand Up @@ -262,6 +263,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 = jest.fn()
const mountSpy = jest.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 () => {
Expand Down
8 changes: 7 additions & 1 deletion packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ function reload(id: string, newComp: HMRComponent) {
// 4. Force the parent instance to re-render. This will cause all updated
// 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.
queueJob(instance.parent.update)
queueJob(() => {
if (instance.parent) {
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()
Expand Down

0 comments on commit 26a6554

Please sign in to comment.