Browse Source

refactor(compiler-vapor): exclude special built-ins from withVaporCtx (#14010)

Also adds documentation to withVaporCtx explaining its purpose and exceptions.
edison 6 months ago
parent
commit
8b4d3dd4cd

+ 12 - 2
packages/compiler-vapor/src/generators/component.ts

@@ -40,7 +40,12 @@ import { genEventHandler } from './event'
 import { genDirectiveModifiers, genDirectivesForElement } from './directive'
 import { genBlock } from './block'
 import { genModelHandler } from './vModel'
-import { isBuiltInComponent, isTransitionTag } from '../utils'
+import {
+  isBuiltInComponent,
+  isKeepAliveTag,
+  isTeleportTag,
+  isTransitionGroupTag,
+} from '../utils'
 
 export function genCreateComponent(
   operation: CreateComponentIRNode,
@@ -460,7 +465,12 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) {
 
   if (
     node.type === NodeTypes.ELEMENT &&
-    (!isBuiltInComponent(node.tag) || isTransitionTag(node.tag))
+    // Not a real component
+    !isTeleportTag(node.tag) &&
+    // Needs to determine whether to activate/deactivate based on instance.parent being KeepAlive
+    !isKeepAliveTag(node.tag) &&
+    // Slot updates need to trigger TransitionGroup's onBeforeUpdate/onUpdated hook
+    !isTransitionGroupTag(node.tag)
   ) {
     // wrap with withVaporCtx to ensure correct currentInstance inside slot
     blockFn = [`${context.helper('withVaporCtx')}(`, ...blockFn, `)`]

+ 14 - 0
packages/runtime-vapor/src/componentSlots.ts

@@ -114,6 +114,20 @@ export function getSlot(
   }
 }
 
+/**
+ * Wraps a slot function to execute in the parent component's context.
+ *
+ * This ensures that:
+ * 1. Reactive effects created inside the slot (e.g., `renderEffect`) bind to the
+ *    parent's instance, so the parent's lifecycle hooks fire when the slot's
+ *    reactive dependencies change.
+ * 2. Elements created in the slot inherit the parent's scopeId for proper style
+ *    scoping in scoped CSS.
+ *
+ * **Rationale**: Slots are defined in the parent's template, so the parent should
+ * own the rendering context and be aware of updates.
+ *
+ */
 export function withVaporCtx(fn: Function): BlockFn {
   const instance = currentInstance as VaporComponentInstance
   return (...args: any[]) => {

+ 1 - 4
packages/runtime-vapor/src/components/TransitionGroup.ts

@@ -61,7 +61,7 @@ export const VaporTransitionGroup: ObjectVaporComponent = decorate({
 
     let prevChildren: TransitionBlock[]
     let children: TransitionBlock[]
-    let slottedBlock: Block
+    const slottedBlock = slots.default && slots.default()
 
     onBeforeUpdate(() => {
       prevChildren = []
@@ -88,7 +88,6 @@ export const VaporTransitionGroup: ObjectVaporComponent = decorate({
       if (!prevChildren.length) {
         return
       }
-
       const moveClass = props.moveClass || `${props.name || 'v'}-move`
       const firstChild = getFirstConnectedChild(prevChildren)
       if (
@@ -122,8 +121,6 @@ export const VaporTransitionGroup: ObjectVaporComponent = decorate({
       prevChildren = []
     })
 
-    slottedBlock = slots.default && slots.default()
-
     // store props and state on fragment for reusing during insert new items
     setTransitionHooksOnFragment(slottedBlock, {
       props: cssTransitionProps,