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

refactor(runtime-vapor): improve DynamicFragment initialization and slot boundary handling

daiwei 3 недель назад
Родитель
Сommit
4c6212dd1b
1 измененных файлов с 30 добавлено и 29 удалено
  1. 30 29
      packages/runtime-vapor/src/fragment.ts

+ 30 - 29
packages/runtime-vapor/src/fragment.ts

@@ -44,7 +44,7 @@ import {
   nextLogicalSibling,
   setCurrentHydrationNode,
 } from './dom/hydration'
-import { isArray } from '@vue/shared'
+import { EMPTY_ARR, isArray } from '@vue/shared'
 import { renderEffect } from './renderEffect'
 import { currentSlotOwner, setCurrentSlotOwner } from './componentSlots'
 import { setBlockKey } from './helpers/setKey'
@@ -62,13 +62,15 @@ import {
   isVaporTransition,
 } from './transition'
 
+const EMPTY_BLOCK = EMPTY_ARR as unknown as Block[]
+
 export class VaporFragment<
   T extends Block = Block,
 > implements TransitionOptions {
   $key?: any
   $transition?: VaporTransitionHooks | undefined
   nodes: T
-  vnode?: VNode | null = null
+  vnode?: VNode | null
   anchor?: Node
   parentComponent?: GenericComponentInstance | null
   // Interop fragments can be visible to outer slot boundaries before their
@@ -83,7 +85,7 @@ export class VaporFragment<
     transitionHooks?: TransitionHooks,
   ) => void
   remove?: (parent?: ParentNode, transitionHooks?: TransitionHooks) => void
-  hydrate?: (...args: any[]) => void
+  hydrate?(...args: any[]): void
   setRef?: (
     instance: VaporComponentInstance,
     ref: NodeRef,
@@ -189,13 +191,12 @@ function getDynamicCloseOwner(
   nodes: Block,
   currentSlotEndAnchor: Node | null,
 ): CloseAnchorOwner {
-  const valid = isValidBlock(nodes)
-
   // Slot fragments own the close marker unless this is an empty forwarded slot.
   // Empty forwarded slots must leave the close marker to the parent boundary
   // and create their runtime anchor after it.
   if (isSlot) {
-    return !forwardedSlot || valid
+    if (!forwardedSlot) return CloseAnchorOwner.Self
+    return isValidBlock(nodes)
       ? CloseAnchorOwner.Self
       : CloseAnchorOwner.ParentAfter
   }
@@ -211,9 +212,9 @@ function getDynamicCloseOwner(
   // its own runtime anchor instead of reusing the parent slot's end anchor.
   if (
     anchorLabel === 'if' &&
-    !valid &&
     currentSlotEndAnchor &&
-    isHydratingSlotFallbackActive()
+    isHydratingSlotFallbackActive() &&
+    !isValidBlock(nodes)
   ) {
     return CloseAnchorOwner.ParentBefore
   }
@@ -251,9 +252,10 @@ export class DynamicFragment extends VaporFragment {
     anchorLabel?: string,
     keyed: boolean = false,
     locate: boolean = true,
+    trackSlotBoundary: boolean = true,
   ) {
-    super([])
-    this.keyed = keyed
+    super(EMPTY_BLOCK)
+    if (keyed) this.keyed = true
     if (
       isTransitionEnabled &&
       currentInstance &&
@@ -269,13 +271,7 @@ export class DynamicFragment extends VaporFragment {
         __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
       if (__DEV__) this.anchorLabel = anchorLabel
     }
-    this.registerSlotBoundaryDirty()
-  }
-
-  protected registerSlotBoundaryDirty(): void {
-    const boundary = this.inheritedSlotBoundary
-    if (!boundary) return
-    ;(this.onUpdated || (this.onUpdated = [])).push(() => boundary.markDirty())
+    if (trackSlotBoundary) trackSlotBoundaryDirtying(this)
   }
 
   update(render?: BlockFn, key: any = render): void {
@@ -294,7 +290,13 @@ export class DynamicFragment extends VaporFragment {
       // leave keep overwriting the pending branch instead of reviving stale
       // keys when the deferred render finally runs.
       this.current = key
-      this.pending = { render, key }
+      const pending = this.pending
+      if (pending) {
+        pending.render = render
+        pending.key = key
+      } else {
+        this.pending = { render, key }
+      }
       return
     }
 
@@ -484,7 +486,7 @@ export class DynamicFragment extends VaporFragment {
       }
     } else {
       this.scope = undefined
-      this.nodes = []
+      this.nodes = EMPTY_BLOCK
     }
 
     if (parent && this.onUpdated) {
@@ -492,7 +494,8 @@ export class DynamicFragment extends VaporFragment {
     }
   }
 
-  hydrate = (isEmpty = false, isSlot = false): void => {
+  // Keep this as a prototype method to avoid per-instance closure allocation.
+  hydrate(isEmpty = false, isSlot = false): void {
     // early return allows tree-shaking of hydration logic when not used
     if (!isHydrating) return
 
@@ -574,7 +577,7 @@ export class DynamicFragment extends VaporFragment {
               ? anchor
               : null
           if (parentNode) {
-            this.nodes = []
+            this.nodes = EMPTY_BLOCK
             if (reusableAnchor) {
               reuseAnchor(reusableAnchor)
             } else {
@@ -602,7 +605,7 @@ export class DynamicFragment extends VaporFragment {
         getParentNode(this.nodes)
       ) {
         const anchor = this.nodes
-        this.nodes = []
+        this.nodes = EMPTY_BLOCK
         reuseAnchor(anchor)
         return
       }
@@ -622,7 +625,7 @@ export class DynamicFragment extends VaporFragment {
         const parentNode = getParentNode(currentHydrationNode)
         const nextNode = nextLogicalSibling(currentHydrationNode)
         if (parentNode) {
-          this.nodes = []
+          this.nodes = EMPTY_BLOCK
           cleanupAndInsertRuntimeAnchor(
             parentNode,
             nextNode,
@@ -634,7 +637,9 @@ export class DynamicFragment extends VaporFragment {
       }
 
       const currentSlotEndAnchor = getCurrentSlotEndAnchor()
-      const forwardedSlot = (this as any as SlotFragment).forwarded
+      const forwardedSlot = isSlot
+        ? (this as any as SlotFragment).forwarded
+        : false
       const slotAnchor = isSlot ? currentSlotEndAnchor : null
 
       // Reuse SSR `<!--]-->` as anchor.
@@ -1330,7 +1335,7 @@ export class SlotFragment
   private _slotFallbackBoundary?: SlotBoundaryContext
 
   constructor() {
-    super(isHydrating || __DEV__ ? 'slot' : undefined, false, false)
+    super(isHydrating || __DEV__ ? 'slot' : undefined, false, false, false)
     if (!isHydrating) {
       this.insert = (parent, anchor) => this.insertSlot(parent, anchor)
     }
@@ -1352,10 +1357,6 @@ export class SlotFragment
     })
   }
 
-  // SlotFragment propagates dirty selectively via recheck() (only when
-  // validity flips), so skip the default auto-register from DynamicFragment.
-  protected registerSlotBoundaryDirty(): void {}
-
   get fallbackBlock(): Block | null {
     return this.activeFallback
   }