]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-vapor): exclude special built-ins from withVaporCtx (#14010)
authoredison <daiwei521@126.com>
Thu, 23 Oct 2025 02:32:51 +0000 (10:32 +0800)
committerGitHub <noreply@github.com>
Thu, 23 Oct 2025 02:32:51 +0000 (10:32 +0800)
Also adds documentation to withVaporCtx explaining its purpose and exceptions.

packages/compiler-vapor/src/generators/component.ts
packages/runtime-vapor/src/componentSlots.ts
packages/runtime-vapor/src/components/TransitionGroup.ts

index f7cbfd3d246341fdd2d068d0de297f7beb92eec6..38dd701704eb7f9f165907b5125187e3278eff4c 100644 (file)
@@ -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, `)`]
index c6bc3c9d0359ffc01e6b346ba44469fb8c126774..b2a3ff5fb97bfba8eaad6b4d46fb74e4f3528278 100644 (file)
@@ -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[]) => {
index 8c54fa25a5746d88b5f00726e5afe18569ea9895..b1e86c87b240a4bc69607a725cca66258aaf0134 100644 (file)
@@ -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,