Skip to content

Commit

Permalink
fix: hmr reload should work with async component
Browse files Browse the repository at this point in the history
  • Loading branch information
KermanX committed Jun 28, 2024
1 parent f2acd51 commit f8b440b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
40 changes: 40 additions & 0 deletions packages/runtime-core/__tests__/hmr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ function compileToFunction(template: string) {
return render
}

const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))

describe('hot module replacement', () => {
test('inject global runtime', () => {
expect(createRecord).toBeDefined()
Expand Down Expand Up @@ -805,4 +807,42 @@ describe('hot module replacement', () => {
`<div><div>1<p>3</p></div></div><div><div>1<p>3</p></div></div><p>2</p>`,
)
})

test('Async component without rerender only', async () => {
const root = nodeOps.createElement('div')
const childId = 'test-child-id'
const Child: ComponentOptions = {
__hmrId: childId,
data() {
return { count: 0 }
},
render: compileToFunction(`<div>{{ count }}</div>`),
}
const Comp = runtimeTest.defineAsyncComponent(() => Promise.resolve(Child))
const appId = 'test-app-id'
const App: ComponentOptions = {
__hmrId: appId,
render: () => [h(Comp), h(Comp)],
}
createRecord(appId, App)

render(h(App), root)

await timeout()

expect(serializeInner(root)).toBe(`<div>0</div><div>0</div>`)

// change count to 1
reload(childId, {
__hmrId: childId,
data() {
return { count: 1 }
},
render: compileToFunction(`<div>{{ count }}</div>`),
})

await timeout()

expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
})
})
10 changes: 7 additions & 3 deletions packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ function reload(id: string, newComp: HMRComponent) {
// create a snapshot which avoids the set being mutated during updates
const instances = [...record.instances]

for (const instance of instances) {
for (let i = 0; i < instances.length; i++) {
const instance = instances[i]
const oldComp = normalizeClassComponent(instance.type as HMRComponent)

if (!hmrDirtyComponents.has(oldComp)) {
Expand Down Expand Up @@ -139,10 +140,13 @@ 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
const isLast = i === instances.length - 1
queueJob(() => {
instance.parent!.update()
// #6930 avoid infinite recursion
hmrDirtyComponents.delete(oldComp)
if (isLast) {
// #6930 avoid infinite recursion
hmrDirtyComponents.delete(oldComp)
}
})
} else if (instance.appContext.reload) {
// root instance mounted via createApp() has a reload method
Expand Down

0 comments on commit f8b440b

Please sign in to comment.