From: daiwei Date: Tue, 12 Aug 2025 09:14:32 +0000 (+0800) Subject: refactor: add anchors for v-else-if branches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=167013eea6413a094b32d93495cd3f5984cc0409;p=thirdparty%2Fvuejs%2Fcore.git refactor: add anchors for v-else-if branches --- diff --git a/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts b/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts index 232b12e25d..f5747b5b32 100644 --- a/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts @@ -130,10 +130,10 @@ describe('insertion anchors', () => { _push(\`\`) } else if (_ctx.bar) { _push(\`\`) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) - _push(\`\`) + _push(\`\`) } _push(\`\`" `) @@ -163,10 +163,10 @@ describe('insertion anchors', () => { _push(\`\`) } else if (_ctx.bar) { _push(\`\`) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) - _push(\`\`) + _push(\`\`) } _push(\`\`) } else { @@ -233,7 +233,7 @@ describe('insertion anchors', () => { _push(\`\`) } _push(\`\`) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) if (_ctx.bar2) { @@ -243,7 +243,7 @@ describe('insertion anchors', () => { _push(\`\`) } _push(\`\`) - _push(\`\`) + _push(\`\`) } _push(\`\`" `) @@ -296,7 +296,7 @@ describe('insertion anchors', () => { _push(\`\`) } _push(\`\`) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) if (_ctx.bar2) { @@ -306,7 +306,7 @@ describe('insertion anchors', () => { _push(\`\`) } _push(\`\`) - _push(\`\`) + _push(\`\`) } _push(\`\`) } else { @@ -500,8 +500,36 @@ describe('insertion anchors', () => { }) }) -describe.todo('block anchors', () => { - test('if', () => {}) +describe('block anchors', () => { + test('if', () => { + expect( + getCompiledString( + `1 + 2 + 3 + 4`, + { + vapor: true, + }, + ), + ).toMatchInlineSnapshot(` + "\`\`) + if (_ctx.count === 1) { + _push(\`1\`) + _push(\`\`) + } else if (_ctx.count === 2) { + _push(\`2\`) + _push(\`\`) + } else if (_ctx.count === 3) { + _push(\`3\`) + _push(\`\`) + } else { + _push(\`4\`) + _push(\`\`) + } + _push(\`\`" + `) + }) test('if in ssr slot vnode fallback', () => {}) @@ -513,6 +541,8 @@ describe.todo('block anchors', () => { test('slot in ssr slot vnode fallback', () => {}) + test('forwarded slot', () => {}) + test('dynamic component', () => {}) test('dynamic in ssr slot vnode fallback', () => {}) diff --git a/packages/compiler-ssr/src/transforms/ssrVIf.ts b/packages/compiler-ssr/src/transforms/ssrVIf.ts index 1470850c70..17ac631138 100644 --- a/packages/compiler-ssr/src/transforms/ssrVIf.ts +++ b/packages/compiler-ssr/src/transforms/ssrVIf.ts @@ -37,6 +37,16 @@ export function ssrProcessIf( ) context.pushStatement(ifStatement) + // anchor addition rules (matching runtime-vapor behavior): + // - v-else-if: the N-th branch → add N anchors + // - v-else: if there are M preceding branches → add M anchors + const isVapor = context.options.vapor + if (isVapor) { + ifStatement.consequent.body.push( + createCallExpression(`_push`, createIfAnchors(1)), + ) + } + let currentIf = ifStatement for (let i = 1; i < node.branches.length; i++) { const branch = node.branches[i] @@ -51,9 +61,21 @@ export function ssrProcessIf( branch.condition, branchBlockStatement, ) + + if (isVapor) { + branchBlockStatement.body.push( + createCallExpression(`_push`, createIfAnchors(i + 1)), + ) + } } else { // else currentIf.alternate = branchBlockStatement + + if (isVapor) { + branchBlockStatement.body.push( + createCallExpression(`_push`, createIfAnchors(i)), + ) + } } } @@ -75,18 +97,14 @@ function processIfBranch( (children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) && // optimize away nested fragments when the only child is a ForNode !(children.length === 1 && children[0].type === NodeTypes.FOR) - const statement = processChildrenAsStatement( - branch, - context, - needFragmentWrapper, - ) - // anchor for vapor v-if/v-else-if - if (context.options.vapor) { - statement.body.push( - createCallExpression(`_push`, [`\`\``]), - ) - } + return processChildrenAsStatement(branch, context, needFragmentWrapper) +} - return statement +function createIfAnchors(count: number): string[] { + const anchors: string[] = [] + for (let i = 0; i < count; i++) { + anchors.push(``) + } + return [`\`${anchors.join('')}\``] } diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap index f83c69bd55..8a83143b9b 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap @@ -61,7 +61,7 @@ export function render(_ctx) { }) n14.$key = 14 return n14 - }, null, true)) + })) return [n0, n3, n7] } }, true) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap index 12b7acd937..272a28bc27 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vIf.spec.ts.snap @@ -36,7 +36,7 @@ export function render(_ctx) { const n10 = t3() const n11 = t4() return [n10, n11] - }, null, true)) + })) const n13 = t5() const x13 = _child(n13, -1) _renderEffect(() => _setText(x13, _toDisplayString(_ctx.text))) @@ -113,7 +113,7 @@ export function render(_ctx) { }, () => { const n7 = t2() return n7 - }, null, true)) + })) return n0 }" `; @@ -130,7 +130,7 @@ export function render(_ctx) { }, () => _createIf(() => (_ctx.orNot), () => { const n4 = t1() return n4 - }, null, null, true)) + })) return n0 }" `; diff --git a/packages/compiler-vapor/src/generators/if.ts b/packages/compiler-vapor/src/generators/if.ts index 3d16bee7c8..f4a3e599fa 100644 --- a/packages/compiler-vapor/src/generators/if.ts +++ b/packages/compiler-vapor/src/generators/if.ts @@ -38,7 +38,6 @@ export function genIf( positiveArg, negativeArg, once && 'true', - isNested && 'true', ), ) diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts index 01a9e3eb3a..26f96a7e45 100644 --- a/packages/runtime-vapor/src/apiCreateIf.ts +++ b/packages/runtime-vapor/src/apiCreateIf.ts @@ -1,4 +1,4 @@ -import { ELSE_IF_ANCHOR_LABEL, IF_ANCHOR_LABEL } from '@vue/shared' +import { IF_ANCHOR_LABEL } from '@vue/shared' import { type Block, type BlockFn, insert } from './block' import { advanceHydrationNode, isHydrating } from './dom/hydration' import { @@ -14,7 +14,6 @@ export function createIf( b1: BlockFn, b2?: BlockFn, once?: boolean, - elseIf?: boolean, ): Block { const _insertionParent = insertionParent const _insertionAnchor = insertionAnchor @@ -26,9 +25,7 @@ export function createIf( } else { frag = isHydrating || __DEV__ - ? new DynamicFragment( - elseIf && isHydrating ? ELSE_IF_ANCHOR_LABEL : IF_ANCHOR_LABEL, - ) + ? new DynamicFragment(IF_ANCHOR_LABEL) : new DynamicFragment() renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2)) } diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index 53fc4340de..6e1cf5ecea 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -23,7 +23,6 @@ import { applyTransitionLeaveHooks, } from './components/Transition' import type { VaporComponentInstance } from './component' -import { ELSE_IF_ANCHOR_LABEL } from '@vue/shared' export class VaporFragment implements TransitionOptions @@ -148,34 +147,25 @@ export class DynamicFragment extends VaporFragment { // avoid repeated hydration during rendering fallback if (this.anchor) return - const createAnchor = () => { - const { parentNode, nextSibling } = findLastChild(this.nodes)! - parentNode!.insertBefore( - // TODO use empty text node in PROD? - (this.anchor = createComment(label)), - nextSibling, - ) - } - - // manually create anchors for: - // 1. else-if branch - // (not present in SSR output) - if (label === ELSE_IF_ANCHOR_LABEL) { - createAnchor() + // for `v-if="false"`, the node will be an empty comment, use it as the anchor. + // otherwise, find next sibling vapor fragment anchor + if (label === 'if' && isEmpty) { + this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, '')! } else { - // for `v-if="false"`, the node will be an empty comment, use it as the anchor. - // otherwise, find next sibling vapor fragment anchor - if (label === 'if' && isEmpty) { - this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, '')! - } else { - this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, label)! - if (!this.anchor && label === 'slot') { - // fallback to fragment end anchor for - this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, ']')! - } + this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, label)! + if (!this.anchor && label === 'slot') { + // fallback to fragment end anchor for + this.anchor = locateVaporFragmentAnchor(currentHydrationNode!, ']')! + } - // anchors are not present in ssr slot vnode fallback - if (!this.anchor) createAnchor() + // anchors are not present in ssr slot vnode fallback + if (!this.anchor) { + const { parentNode, nextSibling } = findLastChild(this.nodes)! + parentNode!.insertBefore( + // TODO use empty text node in PROD? + (this.anchor = createComment(label)), + nextSibling, + ) } } diff --git a/packages/shared/src/domAnchors.ts b/packages/shared/src/domAnchors.ts index fc8bc095c0..25b1b975c9 100644 --- a/packages/shared/src/domAnchors.ts +++ b/packages/shared/src/domAnchors.ts @@ -2,7 +2,6 @@ export const BLOCK_INSERTION_ANCHOR_LABEL = 'i' export const BLOCK_APPEND_ANCHOR_LABEL = 'a' export const BLOCK_PREPEND_ANCHOR_LABEL = 'p' export const IF_ANCHOR_LABEL: string = 'if' -export const ELSE_IF_ANCHOR_LABEL: string = 'else-if' export const DYNAMIC_COMPONENT_ANCHOR_LABEL: string = 'dynamic-component' export const FOR_ANCHOR_LABEL: string = 'for' export const SLOT_ANCHOR_LABEL: string = 'slot'