Просмотр исходного кода

chore: tweak slot parent instance

daiwei 5 месяцев назад
Родитель
Сommit
771c03665f

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformSlotOutlet.spec.ts.snap

@@ -115,7 +115,7 @@ exports[`compiler: transform <slot> outlets > slot outlet with scopeId and slott
 "import { createSlot as _createSlot } from 'vue';
 
 export function render(_ctx) {
-  const n0 = _createSlot("default", null, null, undefined, true)
+  const n0 = _createSlot("default", null, null, true)
   return n0
 }"
 `;

+ 0 - 1
packages/compiler-vapor/src/generators/slotOutlet.ts

@@ -30,7 +30,6 @@ export function genSlotOutlet(
       nameExpr,
       genRawProps(oper.props, context) || 'null',
       fallbackArg,
-      noSlotted && 'undefined', // instance
       noSlotted && 'true', // noSlotted
     ),
   )

+ 6 - 1
packages/runtime-core/src/apiCreateApp.ts

@@ -212,7 +212,12 @@ export interface VaporInteropInterface {
     transition: TransitionHooks,
   ): void
 
-  vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any
+  vdomMount: (
+    component: ConcreteComponent,
+    parentComponent: any,
+    props?: any,
+    slots?: any,
+  ) => any
   vdomUnmount: UnmountComponentFn
   vdomSlot: (
     slots: any,

+ 12 - 4
packages/runtime-vapor/src/block.ts

@@ -255,14 +255,22 @@ export function setScopeId(block: Block, scopeIds: string[]): void {
 }
 
 export function setComponentScopeId(instance: VaporComponentInstance): void {
-  const { parent, slotOwnerScopeId } = instance
-  if (!parent) return
+  const { parent, scopeId } = instance
+  if (!parent || !scopeId) return
+
   // prevent setting scopeId on multi-root fragments
   if (isArray(instance.block) && instance.block.length > 1) return
 
   const scopeIds: string[] = []
-  const scopeId = slotOwnerScopeId || (parent && parent.type.__scopeId)
-  if (scopeId) scopeIds.push(scopeId)
+  const parentScopeId = parent && parent.type.__scopeId
+  // if parent scopeId is different from scopeId, this means scopeId
+  // is inherited from slot owner, so we need to set it to the component
+  // scopeIds. the `parentScopeId-s` is handled in createSlot
+  if (parentScopeId !== scopeId) {
+    scopeIds.push(scopeId)
+  } else {
+    if (parentScopeId) scopeIds.push(parentScopeId)
+  }
 
   // inherit scopeId from vdom parent
   if (

+ 13 - 9
packages/runtime-vapor/src/component.ts

@@ -69,11 +69,10 @@ import {
   type RawSlots,
   type StaticSlots,
   type VaporSlot,
-  currentSlotOwner,
   dynamicSlotsProxyHandlers,
   getParentInstance,
   getSlot,
-  setUseSlotConsumer,
+  setCurrentSlotConsumer,
 } from './componentSlots'
 import { hmrReload, hmrRerender } from './hmr'
 import {
@@ -225,12 +224,14 @@ export function createComponent(
 
   // vdom interop enabled and component is not an explicit vapor component
   if (appContext.vapor && !component.__vapor) {
+    const prevSlotConsumer = setCurrentSlotConsumer(null)
     const frag = appContext.vapor.vdomMount(
       component as any,
+      parentInstance as any,
       rawProps,
       rawSlots,
     )
-
+    setCurrentSlotConsumer(prevSlotConsumer)
     if (!isHydrating) {
       if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
     } else {
@@ -266,6 +267,9 @@ export function createComponent(
     parentInstance,
   )
 
+  // set currentSlotConsumer to null to avoid affecting the child components
+  const prevSlotConsumer = setCurrentSlotConsumer(null)
+
   // HMR
   if (__DEV__ && component.__hmrId) {
     registerHMR(instance)
@@ -324,6 +328,8 @@ export function createComponent(
     setupComponent(instance, component)
   }
 
+  // restore currentSlotConsumer to previous value after setupFn is called
+  setCurrentSlotConsumer(prevSlotConsumer)
   onScopeDispose(() => unmountComponent(instance), true)
 
   if (_insertionParent || isHydrating) {
@@ -333,6 +339,7 @@ export function createComponent(
   if (isHydrating && _insertionAnchor !== undefined) {
     advanceHydrationNode(_insertionParent!)
   }
+
   return instance
 }
 
@@ -477,7 +484,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
 
   slots: StaticSlots
 
-  slotOwnerScopeId?: string | null
+  scopeId?: string | null
 
   // to hold vnode props / slots in vdom interop mode
   rawPropsRef?: ShallowRef<any>
@@ -605,14 +612,12 @@ export class VaporComponentInstance implements GenericComponentInstance {
         : rawSlots
       : EMPTY_OBJ
 
-    this.slotOwnerScopeId = currentSlotOwner && currentSlotOwner.type.__scopeId
+    this.scopeId = currentInstance && currentInstance.type.__scopeId
 
     // apply custom element special handling
     if (comp.ce) {
       comp.ce(this)
     }
-
-    setUseSlotConsumer(false)
   }
 
   /**
@@ -681,8 +686,7 @@ export function createPlainElement(
   ;(el as any).$root = isSingleRoot
 
   if (!isHydrating) {
-    const scopeOwner = currentSlotOwner || currentInstance
-    const scopeId = scopeOwner && scopeOwner.type.__scopeId
+    const scopeId = currentInstance!.type.__scopeId
     if (scopeId) setScopeId(el, [scopeId])
   }
 

+ 16 - 33
packages/runtime-vapor/src/componentSlots.ts

@@ -27,19 +27,15 @@ import { setDynamicProps } from './dom/prop'
 
 /**
  * Current slot scopeIds for vdom interop
- * @internal
  */
 export let currentSlotScopeIds: string[] | null = null
 
-/**
- * @internal
- */
-export function setCurrentSlotScopeIds(
-  scopeIds: string[] | null,
-): string[] | null {
-  const prev = currentSlotScopeIds
-  currentSlotScopeIds = scopeIds
-  return prev
+function setCurrentSlotScopeIds(scopeIds: string[] | null): string[] | null {
+  try {
+    return currentSlotScopeIds
+  } finally {
+    currentSlotScopeIds = scopeIds
+  }
 }
 
 export type RawSlots = Record<string, VaporSlot> & {
@@ -122,18 +118,11 @@ export function getSlot(
   }
 }
 
-export let currentSlotOwner: GenericComponentInstance | null = null
 export let currentSlotConsumer: GenericComponentInstance | null = null
 
-function setCurrentSlotOwner(owner: GenericComponentInstance | null) {
-  try {
-    return currentSlotOwner
-  } finally {
-    currentSlotOwner = owner
-  }
-}
-
-function setCurrentSlotConsumer(consumer: GenericComponentInstance | null) {
+export function setCurrentSlotConsumer(
+  consumer: GenericComponentInstance | null,
+): GenericComponentInstance | null {
   try {
     return currentSlotConsumer
   } finally {
@@ -141,15 +130,13 @@ function setCurrentSlotConsumer(consumer: GenericComponentInstance | null) {
   }
 }
 
-export let useSlotConsumer = false
-export function setUseSlotConsumer(value: boolean): void {
-  useSlotConsumer = value
-}
-
+/**
+ * use currentSlotConsumer as parent, the currentSlotConsumer will be reset to null
+ * before setupFn call to avoid affecting children and restore to previous value
+ * after setupFn is called
+ */
 export function getParentInstance(): GenericComponentInstance | null {
-  // when rendering components in slot, currentInstance is changed in withVaporCtx
-  // should use currentSlotConsumer as parent until new instance is created
-  return useSlotConsumer ? currentSlotConsumer : currentInstance
+  return currentSlotConsumer || currentInstance
 }
 
 /**
@@ -160,17 +147,13 @@ export function getParentInstance(): GenericComponentInstance | null {
 export function withVaporCtx(fn: Function): BlockFn {
   const owner = currentInstance
   return (...args: any[]) => {
-    useSlotConsumer = true
     const prev = setCurrentInstance(owner)
-    const prevOwner = setCurrentSlotOwner(owner)
     const prevConsumer = setCurrentSlotConsumer(prev[0])
     try {
       return fn(...args)
     } finally {
-      setCurrentSlotConsumer(prevConsumer)
-      setCurrentSlotOwner(prevOwner)
       setCurrentInstance(...prev)
-      useSlotConsumer = false
+      setCurrentSlotConsumer(prevConsumer)
     }
   }
 }

+ 12 - 17
packages/runtime-vapor/src/vdomInterop.ts

@@ -59,11 +59,7 @@ import {
 } from '@vue/shared'
 import { type RawProps, rawPropsProxyHandlers } from './componentProps'
 import type { RawSlots, VaporSlot } from './componentSlots'
-import {
-  currentSlotOwner,
-  currentSlotScopeIds,
-  getParentInstance,
-} from './componentSlots'
+import { currentSlotScopeIds } from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { _next, createTextNode } from './dom/node'
 import { optimizePropertyLookup } from './dom/prop'
@@ -277,10 +273,10 @@ let vdomHydrateNode: HydrationRenderer['hydrateNode'] | undefined
 function createVDOMComponent(
   internals: RendererInternals,
   component: ConcreteComponent,
+  parentComponent: VaporComponentInstance | null,
   rawProps?: LooseRawProps | null,
   rawSlots?: LooseRawSlots | null,
 ): VaporFragment {
-  const parentInstance = getParentInstance() as VaporComponentInstance | null
   const frag = new VaporFragment([])
   const vnode = (frag.vnode = createVNode(
     component,
@@ -290,9 +286,9 @@ function createVDOMComponent(
     { props: component.props },
     rawProps as RawProps,
     rawSlots as RawSlots,
-    parentInstance ? parentInstance.appContext : undefined,
+    parentComponent ? parentComponent.appContext : undefined,
     undefined,
-    parentInstance,
+    parentComponent,
   )
 
   // overwrite how the vdom instance handles props
@@ -322,9 +318,9 @@ function createVDOMComponent(
     if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
       vdomDeactivate(
         vnode,
-        findParentKeepAlive(parentInstance!)!.getStorageContainer(),
+        findParentKeepAlive(parentComponent!)!.getStorageContainer(),
         internals,
-        parentInstance as any,
+        parentComponent as any,
         null,
       )
       return
@@ -333,14 +329,13 @@ function createVDOMComponent(
   }
 
   frag.hydrate = () => {
-    hydrateVNode(vnode, parentInstance as any)
+    hydrateVNode(vnode, parentComponent as any)
     onScopeDispose(unmount, true)
     isMounted = true
     frag.nodes = vnode.el as any
   }
 
-  const scopeOwner = currentSlotOwner || parentInstance
-  vnode.scopeId = (scopeOwner && scopeOwner.type.__scopeId) || null
+  vnode.scopeId = (currentInstance && currentInstance.type.__scopeId) || null
   vnode.slotScopeIds = currentSlotScopeIds
 
   frag.insert = (parentNode, anchor, transition) => {
@@ -351,21 +346,21 @@ function createVDOMComponent(
         parentNode,
         anchor,
         internals,
-        parentInstance as any,
+        parentComponent as any,
         null,
         undefined,
         false,
       )
     } else {
       const prev = currentInstance
-      simpleSetCurrentInstance(parentInstance)
+      simpleSetCurrentInstance(parentComponent)
       if (!isMounted) {
         if (transition) setVNodeTransitionHooks(vnode, transition)
         internals.mt(
           vnode,
           parentNode,
           anchor,
-          parentInstance as any,
+          parentComponent as any,
           null,
           undefined,
           false,
@@ -381,7 +376,7 @@ function createVDOMComponent(
           parentNode,
           anchor,
           MoveType.REORDER,
-          parentInstance as any,
+          parentComponent as any,
         )
       }
       simpleSetCurrentInstance(prev)