]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-vapor): add support for forwarded slots
authordaiwei <daiwei521@126.com>
Thu, 29 May 2025 09:04:49 +0000 (17:04 +0800)
committerdaiwei <daiwei521@126.com>
Thu, 29 May 2025 09:04:49 +0000 (17:04 +0800)
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/slotOutlet.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transform.ts
packages/compiler-vapor/src/transforms/transformSlotOutlet.ts
packages/runtime-vapor/src/componentSlots.ts
packages/runtime-vapor/src/index.ts

index 193a0f5da777be3f443ac9aa2480870aad6a3d34..ff3806611addf37b4f0dc8351306c8260bf22204 100644 (file)
@@ -18,6 +18,7 @@ import {
   genCall,
 } from './generators/utils'
 import { setTemplateRefIdent } from './generators/templateRef'
+import { createForwardedSlotIdent } from './generators/slotOutlet'
 
 export type CodegenOptions = Omit<BaseCodegenOptions, 'optimizeImports'>
 
@@ -129,6 +130,12 @@ export function generate(
       `const ${setTemplateRefIdent} = ${context.helper('createTemplateRefSetter')}()`,
     )
   }
+  if (ir.hasForwardedSlot) {
+    push(
+      NEWLINE,
+      `const ${createForwardedSlotIdent} = ${context.helper('forwardedSlotCreator')}()`,
+    )
+  }
   push(...genBlockContent(ir.block, context, true))
   push(INDENT_END, NEWLINE)
 
index 3221cbbd2c75e4f44a95ad6d903f29a195a91b95..dc992ae23347699bb65869bdc5f57dbec9c7e50e 100644 (file)
@@ -5,12 +5,14 @@ import { genExpression } from './expression'
 import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
 import { genRawProps } from './component'
 
+export const createForwardedSlotIdent = `_createForwardedSlot`
+
 export function genSlotOutlet(
   oper: SlotOutletIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
   const { helper } = context
-  const { id, name, fallback } = oper
+  const { id, name, fallback, forwarded } = oper
   const [frag, push] = buildCodeFragment()
 
   const nameExpr = name.isStatic
@@ -26,7 +28,7 @@ export function genSlotOutlet(
     NEWLINE,
     `const n${id} = `,
     ...genCall(
-      helper('createSlot'),
+      forwarded ? createForwardedSlotIdent : helper('createSlot'),
       nameExpr,
       genRawProps(oper.props, context) || 'null',
       fallbackArg,
index da636113224c3bc7685b9c4de6d95ddfcd4f623e..086f77ca61246d92ffbad30cf6e87cc4a72e9373 100644 (file)
@@ -66,6 +66,7 @@ export interface RootIRNode {
   directive: Set<string>
   block: BlockIRNode
   hasTemplateRef: boolean
+  hasForwardedSlot: boolean
 }
 
 export interface IfIRNode extends BaseIRNode {
@@ -209,6 +210,7 @@ export interface SlotOutletIRNode extends BaseIRNode {
   name: SimpleExpressionNode
   props: IRProps[]
   fallback?: BlockIRNode
+  forwarded?: boolean
   parent?: number
   anchor?: number
 }
index 76563899d2b770b19aae215805cded9c13ba85b5..93488ae95a160e5d11efe9493131c7cb96698557 100644 (file)
@@ -230,6 +230,7 @@ export function transform(
     directive: new Set(),
     block: newBlock(node),
     hasTemplateRef: false,
+    hasForwardedSlot: false,
   }
 
   const context = new TransformContext(ir, node, options)
index 83b4aa2d2e4a565d63f68fe97369f648f83258a2..159d70c3814b258a1dced5b335282480249550f9 100644 (file)
@@ -5,6 +5,7 @@ import {
   ErrorCodes,
   NodeTypes,
   type SimpleExpressionNode,
+  type TemplateChildNode,
   createCompilerError,
   createSimpleExpression,
   isStaticArgOf,
@@ -99,6 +100,13 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
   }
 
   return () => {
+    let forwarded = false
+    const slotNode = context.block.node
+    if (slotNode.type === NodeTypes.ELEMENT) {
+      forwarded = hasForwardedSlots(slotNode.children)
+    }
+    if (forwarded) context.ir.hasForwardedSlot = true
+
     exitBlock && exitBlock()
     context.dynamic.operation = {
       type: IRNodeTypes.SLOT_OUTLET_NODE,
@@ -106,6 +114,7 @@ export const transformSlotOutlet: NodeTransform = (node, context) => {
       name: slotName,
       props: irProps,
       fallback,
+      forwarded,
     }
   }
 }
@@ -131,3 +140,21 @@ function createFallback(
   context.reference()
   return [fallback, exitBlock]
 }
+
+// TODO
+function hasForwardedSlots(children: TemplateChildNode[]): boolean {
+  for (let i = 0; i < children.length; i++) {
+    const child = children[i]
+    switch (child.type) {
+      case NodeTypes.ELEMENT:
+        if (
+          child.tagType === ElementTypes.SLOT ||
+          hasForwardedSlots(child.children)
+        ) {
+          return true
+        }
+        break
+    }
+  }
+  return false
+}
index 74296e09466359d9fddc37305b032b4e99741010..00ae4ea29ac75959620dfde6db8377fd95776035 100644 (file)
@@ -87,10 +87,24 @@ export function getSlot(
   }
 }
 
+export function forwardedSlotCreator(): (
+  name: string | (() => string),
+  rawProps?: LooseRawProps | null,
+  fallback?: VaporSlot,
+) => Block {
+  const instance = currentInstance as VaporComponentInstance
+  return (
+    name: string | (() => string),
+    rawProps?: LooseRawProps | null,
+    fallback?: VaporSlot,
+  ) => createSlot(name, rawProps, fallback, instance)
+}
+
 export function createSlot(
   name: string | (() => string),
   rawProps?: LooseRawProps | null,
   fallback?: VaporSlot,
+  i?: VaporComponentInstance,
 ): Block {
   const _insertionParent = insertionParent
   const _insertionAnchor = insertionAnchor
@@ -98,7 +112,7 @@ export function createSlot(
     locateHydrationNode()
   }
 
-  const instance = currentInstance as VaporComponentInstance
+  const instance = i || (currentInstance as VaporComponentInstance)
   const rawSlots = instance.rawSlots
   const slotProps = rawProps
     ? new Proxy(rawProps, rawPropsProxyHandlers)
index 682532fa4d80aa01a23a948bbb538b049715a9ff..10d0aa63384cd085de2546760e7c0fc7db8fc521 100644 (file)
@@ -9,7 +9,7 @@ export { insert, prepend, remove, isFragment, VaporFragment } from './block'
 export { setInsertionState } from './insertionState'
 export { createComponent, createComponentWithFallback } from './component'
 export { renderEffect } from './renderEffect'
-export { createSlot } from './componentSlots'
+export { createSlot, forwardedSlotCreator } from './componentSlots'
 export { template } from './dom/template'
 export { createTextNode, child, nthChild, next } from './dom/node'
 export {