Bladeren bron

fix(runtime-vapor): preserve slot owner to ensure correct scopeId inheritance for nested components within v-for loops with slots. (#14353)

edison 3 maanden geleden
bovenliggende
commit
cb2a17c81d
2 gewijzigde bestanden met toevoegingen van 79 en 2 verwijderingen
  1. 70 1
      packages/runtime-vapor/__tests__/scopeId.spec.ts
  2. 9 1
      packages/runtime-vapor/src/apiCreateFor.ts

+ 70 - 1
packages/runtime-vapor/__tests__/scopeId.spec.ts

@@ -1,7 +1,8 @@
-import { createApp, h } from '@vue/runtime-dom'
+import { createApp, h, nextTick, ref } from '@vue/runtime-dom'
 import {
   createComponent,
   createDynamicComponent,
+  createFor,
   createSlot,
   defineVaporComponent,
   setInsertionState,
@@ -374,6 +375,74 @@ describe('scopeId', () => {
         `</div>`,
     )
   })
+
+  test('nested components in vFor with slots', async () => {
+    const Parent = defineVaporComponent({
+      setup() {
+        const n1 = template('<div>', true)() as any
+        setInsertionState(n1, null, 0, true)
+        createSlot('default', null)
+        return n1
+      },
+    })
+
+    const Child = defineVaporComponent({
+      setup() {
+        const n1 = template('<div>', true)() as any
+        setInsertionState(n1, null, 0, true)
+        createSlot('default', null)
+        return n1
+      },
+    })
+
+    const count = ref(0)
+    const { html } = define({
+      __scopeId: 'app',
+      setup() {
+        const n4 = createComponent(
+          Parent,
+          null,
+          {
+            default: withVaporCtx(() => {
+              const n0 = createFor(
+                () => count.value,
+                _for_item0 => {
+                  const n3 = createComponent(
+                    Child,
+                    { class: () => 'test' },
+                    {
+                      default: () => {
+                        const n2 = template('<div> red ')()
+                        return n2
+                      },
+                    },
+                  )
+                  return n3
+                },
+                item => item,
+                2,
+              )
+              return n0
+            }),
+          },
+          true,
+        )
+        return n4
+      },
+    }).render()
+
+    expect(html()).toBe(`<div app=""><!--for--><!--slot--></div>`)
+
+    count.value++
+    await nextTick()
+    expect(html()).toBe(
+      `<div app="">` +
+        `<div class="test" app="">` + // should have app scopeId
+        `<div> red </div><!--slot-->` +
+        `</div><!--for-->` +
+        `<!--slot--></div>`,
+    )
+  })
 })
 
 describe('vdom interop', () => {

+ 9 - 1
packages/runtime-vapor/src/apiCreateFor.ts

@@ -22,7 +22,11 @@ import {
 } from './block'
 import { warn } from '@vue/runtime-dom'
 import { currentInstance, isVaporComponent } from './component'
-import type { DynamicSlot } from './componentSlots'
+import {
+  type DynamicSlot,
+  currentSlotOwner,
+  setCurrentSlotOwner,
+} from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
 import {
@@ -123,6 +127,8 @@ export const createFor = (
     cleanup: () => void
   }[] = []
 
+  const scopeOwner = currentSlotOwner
+
   if (__DEV__ && !instance) {
     warn('createFor() can only be used inside setup()')
   }
@@ -167,6 +173,7 @@ export const createFor = (
         }
       }
     } else {
+      const prevOwner = setCurrentSlotOwner(scopeOwner)
       parent = parent || parentAnchor!.parentNode
       if (!oldLength) {
         // remove fallback nodes
@@ -394,6 +401,7 @@ export const createFor = (
           }
         }
       }
+      setCurrentSlotOwner(prevOwner)
     }
 
     if (!isFallback) {