]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hmr): prevent updating unmounting component during HMR rerender (#13775)
authoredison <daiwei521@126.com>
Wed, 20 Aug 2025 12:49:59 +0000 (20:49 +0800)
committerGitHub <noreply@github.com>
Wed, 20 Aug 2025 12:49:59 +0000 (20:49 +0800)
close #13771
close #13772

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

index 3f157d009a93e247a18ff210c9ae585c8438e696..01feb91aceed7f31eb8553a5684c8344ab3cda62 100644 (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')
+  })
 })
index 7aedf52dd3ea562045200e411947c7ca937aa90f..2c46ea73b502515f3f2678326759aa5a8f63d3ad 100644 (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
   })
 }