From bf78e867618ea4448afc24c78b44dd20ad191dfb Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 5 Dec 2025 17:39:33 +0800 Subject: [PATCH 1/2] refactor(runtime-vapor): prevent duplicate fragment fallback processing and improve invalid fragment detection. --- packages/runtime-vapor/src/fragment.ts | 57 ++++++++++++++++++-------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index f80f0761176..572fdbe2195 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -151,20 +151,31 @@ export class DynamicFragment extends VaporFragment { this.render(render, transition, parent, instance) if (this.fallback) { - // set fallback for nested fragments - const hasNestedFragment = isFragment(this.nodes) - if (hasNestedFragment) { - setFragmentFallback(this.nodes as VaporFragment, this.fallback) + // Find the deepest invalid fragment + let invalidFragment: VaporFragment | null = null + if (isFragment(this.nodes)) { + setFragmentFallback( + this.nodes, + this.fallback, + (frag: VaporFragment) => { + if (!isValidBlock(frag.nodes)) { + invalidFragment = frag + } + }, + ) + } + + // Check self validity (when no nested fragment or nested is valid) + if (!invalidFragment && !isValidBlock(this.nodes)) { + invalidFragment = this } - const invalidFragment = findInvalidFragment(this) if (invalidFragment) { parent && remove(this.nodes, parent) const scope = this.scope || (this.scope = new EffectScope()) scope.run(() => { - // for nested fragments, render invalid fragment's fallback - if (hasNestedFragment) { - renderFragmentFallback(invalidFragment) + if (invalidFragment !== this) { + renderFragmentFallback(invalidFragment!) } else { this.nodes = this.fallback!() || [] } @@ -294,10 +305,28 @@ export class DynamicFragment extends VaporFragment { } } +// Track which fallback has been set on each fragment +// to avoid duplicate chaining when nested DynamicFragments call setFragmentFallback +const processedFallbacks = new WeakMap() + export function setFragmentFallback( fragment: VaporFragment, fallback: BlockFn, + onFragment?: (frag: VaporFragment) => void, ): void { + if (processedFallbacks.get(fragment) === fallback) { + // Already processed with this fallback, skip re-setting + // but still recurse since nodes may have changed + if (onFragment) onFragment(fragment) + if (isFragment(fragment.nodes)) { + setFragmentFallback(fragment.nodes, fragment.fallback!, onFragment) + } + return + } + + // Mark as processed + processedFallbacks.set(fragment, fallback) + if (fragment.fallback) { const originalFallback = fragment.fallback // if the original fallback also renders invalid blocks, @@ -313,8 +342,10 @@ export function setFragmentFallback( fragment.fallback = fallback } + if (onFragment) onFragment(fragment) + if (isFragment(fragment.nodes)) { - setFragmentFallback(fragment.nodes, fragment.fallback) + setFragmentFallback(fragment.nodes, fragment.fallback, onFragment) } } @@ -328,14 +359,6 @@ function renderFragmentFallback(fragment: VaporFragment): void { } } -function findInvalidFragment(fragment: VaporFragment): VaporFragment | null { - if (isValidBlock(fragment.nodes)) return null - - return isFragment(fragment.nodes) - ? findInvalidFragment(fragment.nodes) || fragment - : fragment -} - export function isFragment(val: NonNullable): val is VaporFragment { return val instanceof VaporFragment } From 6b8693d38a005fbb6a54b66035efa158fe9a7737 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 5 Dec 2025 20:47:48 +0800 Subject: [PATCH 2/2] refactor(runtime-vapor): remove duplicate fallback processing tracking --- packages/runtime-vapor/src/fragment.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index 572fdbe2195..41bfde220b1 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -305,28 +305,11 @@ export class DynamicFragment extends VaporFragment { } } -// Track which fallback has been set on each fragment -// to avoid duplicate chaining when nested DynamicFragments call setFragmentFallback -const processedFallbacks = new WeakMap() - export function setFragmentFallback( fragment: VaporFragment, fallback: BlockFn, onFragment?: (frag: VaporFragment) => void, ): void { - if (processedFallbacks.get(fragment) === fallback) { - // Already processed with this fallback, skip re-setting - // but still recurse since nodes may have changed - if (onFragment) onFragment(fragment) - if (isFragment(fragment.nodes)) { - setFragmentFallback(fragment.nodes, fragment.fallback!, onFragment) - } - return - } - - // Mark as processed - processedFallbacks.set(fragment, fallback) - if (fragment.fallback) { const originalFallback = fragment.fallback // if the original fallback also renders invalid blocks,