fix(hmr): prevent updating unmounting component during HMR rerender (#13775)

close #13771
close #13772
This commit is contained in:
edison 2025-08-20 20:49:59 +08:00 committed by GitHub
parent 1498821ed9
commit 6e5143d963
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 2 deletions

View File

@ -894,4 +894,47 @@ describe('hot module replacement', () => {
await timeout()
expect(serializeInner(root)).toBe('<div>bar</div>')
})
test('rerender for nested component', () => {
const id = 'child-nested-rerender'
const Foo: ComponentOptions = {
__hmrId: id,
render() {
return this.$slots.default()
},
}
createRecord(id, Foo)
const parentId = 'parent-nested-rerender'
const Parent: ComponentOptions = {
__hmrId: parentId,
render() {
return h(Foo, null, {
default: () => this.$slots.default(),
_: 3 /* FORWARDED */,
})
},
}
const appId = 'app-nested-rerender'
const App: ComponentOptions = {
__hmrId: appId,
render: () =>
h(Parent, null, {
default: () => [
h(Foo, null, {
default: () => ['foo'],
}),
],
}),
}
createRecord(parentId, App)
const root = nodeOps.createElement('div')
render(h(App), root)
expect(serializeInner(root)).toBe('foo')
rerender(id, () => 'bar')
expect(serializeInner(root)).toBe('bar')
})
})

View File

@ -7,7 +7,7 @@ import {
type InternalRenderFunction,
isClassComponent,
} from './component'
import { queueJob, queuePostFlushCb } from './scheduler'
import { SchedulerJobFlags, queueJob, queuePostFlushCb } from './scheduler'
import { extend, getGlobalThis } from '@vue/shared'
type HMRComponent = ComponentOptions | ClassComponent
@ -96,7 +96,10 @@ function rerender(id: string, newRender?: Function): void {
instance.renderCache = []
// this flag forces child components with slot content to update
isHmrUpdating = true
instance.update()
// #13771 don't update if the job is already disposed
if (!(instance.job.flags! & SchedulerJobFlags.DISPOSED)) {
instance.update()
}
isHmrUpdating = false
})
}