diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index 9ffdf6dca..6153a815f 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -15,8 +15,10 @@ import { isArray, isObject, isString } from '@vue/shared' import { createComment, createTextNode } from './dom/node' import { type Block, + ForFragment, VaporFragment, insert, + remove, remove as removeBlock, } from './block' import { warn } from '@vue/runtime-dom' @@ -77,7 +79,7 @@ export const createFor = ( setup?: (_: { createSelector: (source: () => any) => (cb: () => void) => void }) => void, -): VaporFragment => { +): ForFragment => { const _insertionParent = insertionParent const _insertionAnchor = insertionAnchor if (isHydrating) { @@ -94,7 +96,7 @@ export const createFor = ( let currentKey: any // TODO handle this in hydration const parentAnchor = __DEV__ ? createComment('for') : createTextNode() - const frag = new VaporFragment(oldBlocks) + const frag = new ForFragment(oldBlocks) const instance = currentInstance! const canUseFastRemove = !!(flags & VaporVForFlags.FAST_REMOVE) const isComponent = !!(flags & VaporVForFlags.IS_COMPONENT) @@ -123,6 +125,14 @@ export const createFor = ( } else { parent = parent || parentAnchor!.parentNode if (!oldLength) { + // remove fallback nodes if needed + if (frag.fallback) { + const fallbackNodes = frag.nodes[0] as any + if (fallbackNodes) { + remove(fallbackNodes, parent!) + } + } + // fast path for all new for (let i = 0; i < newLength; i++) { mount(source, i) @@ -329,6 +339,11 @@ export const createFor = ( frag.nodes.push(parentAnchor) } + // render fallback nodes if needed + if (isMounted && frag.fallback && newLength === 0) { + insert((frag.nodes[0] = frag.fallback()), parent!, parentAnchor) + } + setActiveSub(prevSub) } diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 2a78048f8..0495c58e5 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -18,17 +18,24 @@ export type Block = export type BlockFn = (...args: any[]) => Block -export class VaporFragment { - nodes: Block +export class VaporFragment { + nodes: T anchor?: Node insert?: (parent: ParentNode, anchor: Node | null) => void remove?: (parent?: ParentNode) => void + fallback?: BlockFn - constructor(nodes: Block) { + constructor(nodes: T) { this.nodes = nodes } } +export class ForFragment extends VaporFragment { + constructor(nodes: Block[]) { + super(nodes) + } +} + export class DynamicFragment extends VaporFragment { anchor: Node scope: EffectScope | undefined @@ -67,14 +74,16 @@ export class DynamicFragment extends VaporFragment { if (this.fallback && !isValidBlock(this.nodes)) { parent && remove(this.nodes, parent) - // handle nested dynamic fragment - if (isFragment(this.nodes)) { - renderFallback(this.nodes, this.fallback, key) - } else { - this.nodes = - (this.scope || (this.scope = new EffectScope())).run(this.fallback) || - [] - } + const scope = this.scope || (this.scope = new EffectScope()) + scope.run(() => { + // handle nested fragment + if (isFragment(this.nodes)) { + renderFallback(this.nodes, this.fallback!) + } else { + this.nodes = this.fallback!() || [] + } + }) + parent && insert(this.nodes, parent, this.anchor) } @@ -82,19 +91,21 @@ export class DynamicFragment extends VaporFragment { } } -function renderFallback( - fragment: VaporFragment, - fallback: BlockFn, - key: any, -): void { +function renderFallback(fragment: VaporFragment, fallback: BlockFn): void { + if (!fragment.fallback) fragment.fallback = fallback + if (fragment instanceof DynamicFragment) { const nodes = fragment.nodes if (isFragment(nodes)) { - renderFallback(nodes, fallback, key) + renderFallback(nodes, fallback) } else { - if (!fragment.fallback) fragment.fallback = fallback - fragment.update(fragment.fallback, key) + fragment.update(fragment.fallback) } + } else if (fragment instanceof ForFragment) { + // TODO handle nested ForFragment + fragment.nodes[0] = fallback() || [] + } else { + // vdom slots } }