Bläddra i källkod

fix(runtime-vapor): preserve setup effects during hmr rerender

daiwei 2 veckor sedan
förälder
incheckning
e8fc5d4d93

+ 37 - 0
packages/runtime-vapor/__tests__/hmr.spec.ts

@@ -16,6 +16,7 @@ import {
   setCurrentInstance,
   toDisplayString,
   warn,
+  watchEffect,
 } from '@vue/runtime-dom'
 import { compileToVaporRender as compileToFunction, makeRender } from './_utils'
 import {
@@ -183,6 +184,42 @@ describe('hot module replacement', () => {
     expect(mountSpy).toHaveBeenCalledTimes(1)
   })
 
+  test('reload child should preserve parent setup effects', async () => {
+    const root = document.createElement('div')
+    const childId = 'test-reload-child-preserve-parent-effects'
+    const parentCount = ref(0)
+    const spy = vi.fn()
+
+    const Child = defineVaporComponent({
+      __hmrId: childId,
+      render: () => template('<div>old</div>')(),
+    })
+    createRecord(childId, Child as any)
+
+    const Parent = defineVaporComponent({
+      setup() {
+        watchEffect(() => spy(parentCount.value))
+      },
+      render: () => createComponent(Child),
+    })
+
+    createVaporApp(Parent).mount(root)
+    expect(root.innerHTML).toBe(`<div>old</div>`)
+    expect(spy).toHaveBeenLastCalledWith(0)
+
+    reload(childId, {
+      __vapor: true,
+      __hmrId: childId,
+      render: () => template('<div>new</div>')(),
+    })
+    await nextTick()
+    expect(root.innerHTML).toBe(`<div>new</div>`)
+
+    parentCount.value++
+    await nextTick()
+    expect(spy).toHaveBeenLastCalledWith(1)
+  })
+
   test('reload root vapor component should preserve appContext provide/inject', async () => {
     const root = document.createElement('div')
     const appId = 'test-root-reload-app-context'

+ 1 - 1
packages/runtime-vapor/src/component.ts

@@ -708,7 +708,7 @@ export class VaporComponentInstance<
   restoreAsyncContext?: () => void | (() => void)
   deferredHydrationBoundary?: () => void
 
-  // for vapor custom element
+  // for vapor custom element and hmr rerender
   renderEffects?: RenderEffect[]
 
   hasFallthrough: boolean

+ 4 - 2
packages/runtime-vapor/src/hmr.ts

@@ -16,8 +16,10 @@ import {
 export function hmrRerender(instance: VaporComponentInstance): void {
   const { parentNode, nextNode: anchor } = findBlockBoundary(instance.block)
   const parent = parentNode as ParentNode
-  // reset scope to avoid stale effects
-  instance.scope.reset()
+  if (instance.renderEffects) {
+    instance.renderEffects.forEach(e => e.stop())
+    instance.renderEffects.length = 0
+  }
   remove(instance.block, parent)
   const prev = setCurrentInstance(instance)
   pushWarningContext(instance)

+ 3 - 3
packages/runtime-vapor/src/renderEffect.ts

@@ -44,9 +44,9 @@ export class RenderEffect extends ReactiveEffect {
           : void 0
       }
 
-      // register effect for vapor custom element update
-      if (instance.type.ce) {
-        ;(instance.renderEffects || (instance.renderEffects = [])).push(this)
+      // register effect for vapor custom element update and dev HMR cleanup
+      if (instance.type.ce || __DEV__) {
+        ;(instance.renderEffects ||= []).push(this)
       }
       job.i = instance
     }