Prechádzať zdrojové kódy

fix(hmr): reload async child wrapped in Suspense + KeepAlive (#11907)

close #11868
edison 1 rok pred
rodič
commit
10a2c6053b

+ 43 - 0
packages/runtime-core/__tests__/hmr.spec.ts

@@ -851,4 +851,47 @@ describe('hot module replacement', () => {
 
     expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
   })
+
+  test('reload async child wrapped in Suspense + KeepAlive', async () => {
+    const id = 'async-child-reload'
+    const AsyncChild: ComponentOptions = {
+      __hmrId: id,
+      async setup() {
+        await nextTick()
+        return () => 'foo'
+      },
+    }
+    createRecord(id, AsyncChild)
+
+    const appId = 'test-app-id'
+    const App: ComponentOptions = {
+      __hmrId: appId,
+      components: { AsyncChild },
+      render: compileToFunction(`
+        <div>
+        <Suspense>
+          <KeepAlive>
+            <AsyncChild />
+          </KeepAlive>
+        </Suspense>
+      </div>
+      `),
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(App), root)
+    expect(serializeInner(root)).toBe('<div><!----></div>')
+    await timeout()
+    expect(serializeInner(root)).toBe('<div>foo</div>')
+
+    reload(id, {
+      __hmrId: id,
+      async setup() {
+        await nextTick()
+        return () => 'bar'
+      },
+    })
+    await timeout()
+    expect(serializeInner(root)).toBe('<div>bar</div>')
+  })
 })

+ 2 - 0
packages/runtime-core/src/hmr.ts

@@ -144,7 +144,9 @@ function reload(id: string, newComp: HMRComponent): void {
       // 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(() => {
+        isHmrUpdating = true
         instance.parent!.update()
+        isHmrUpdating = false
         // #6930, #11248 avoid infinite recursion
         dirtyInstances.delete(instance)
       })

+ 3 - 0
packages/runtime-core/src/renderer.ts

@@ -1211,6 +1211,9 @@ function baseCreateRenderer(
     // setup() is async. This component relies on async logic to be resolved
     // before proceeding
     if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
+      // avoid hydration for hmr updating
+      if (__DEV__ && isHmrUpdating) initialVNode.el = null
+
       parentSuspense &&
         parentSuspense.registerDep(instance, setupRenderEffect, optimized)