Bläddra i källkod

fix(runtime-vapor): preserve async wrapper keys across dynamic fragment updates

daiwei 1 månad sedan
förälder
incheckning
fea9fd9676

+ 41 - 0
packages/runtime-vapor/__tests__/components/TransitionGroup.spec.ts

@@ -2,14 +2,17 @@ import {
   VaporTransitionGroup,
   createComponent,
   createIf,
+  defineVaporAsyncComponent,
   defineVaporComponent,
   setBlockKey,
   template,
   withVaporCtx,
 } from '../../src'
+import { nextTick } from '@vue/runtime-dom'
 import { makeRender } from '../_utils'
 
 const define = makeRender()
+const timeout = (n = 0) => new Promise(r => setTimeout(r, n))
 
 describe('TransitionGroup', () => {
   test('inherits outer component key for a single transition child', () => {
@@ -85,4 +88,42 @@ describe('TransitionGroup', () => {
 
     expect(`<transition-group> children must be keyed`).toHaveBeenWarnedTimes(2)
   })
+
+  test('preserves outer key when unresolved async child resolves', async () => {
+    let resolve!: (comp: any) => void
+    const ResolvedChild = defineVaporComponent({
+      setup() {
+        return template(`<div>child</div>`)() as any
+      },
+    })
+    const AsyncChild = defineVaporAsyncComponent(
+      () =>
+        new Promise(r => {
+          resolve = r as any
+        }),
+    )
+
+    let child: any
+    define({
+      setup() {
+        child = createComponent(AsyncChild)
+        setBlockKey(child, 'foo')
+        return createComponent(VaporTransitionGroup, null, {
+          default: withVaporCtx(() => child),
+        })
+      },
+    }).render()
+
+    expect(child.$key).toBe('foo')
+    expect(child.block.$key).toBe('foo')
+
+    resolve(ResolvedChild)
+    await timeout()
+    await nextTick()
+    await nextTick()
+
+    expect(child.block.nodes.$key).toBe('foo')
+    expect(child.block.nodes.block.$key).toBe('foo')
+    expect(child.block.nodes.block.$transition).toBeDefined()
+  })
 })

+ 3 - 2
packages/runtime-vapor/src/fragment.ts

@@ -237,8 +237,9 @@ export class DynamicFragment extends VaporFragment {
             () => this.scope!.run(render) || [],
           )
         } finally {
-          // set key on blocks
-          if (this.keyed) setBlockKey(this.nodes, this.current)
+          // propagate the fragment key onto freshly rendered nodes.
+          const key = this.keyed ? this.current : this.$key
+          if (key !== undefined) setBlockKey(this.nodes, key)
 
           if (transition) {
             this.$transition = applyTransitionHooks(this.nodes, transition)