_push(\`<!--if-->\`)
} else if (_ctx.bar) {
_push(\`<span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
} else {
_push(\`<span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
}
_push(\`<!--p]--><span></span></div>\`"
`)
_push(\`<!--if-->\`)
} else if (_ctx.bar) {
_push(\`<span\${_scopeId}></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
} else {
_push(\`<span\${_scopeId}></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
}
_push(\`<!--p]--><span\${_scopeId}></span></div>\`)
} else {
_push(\`<!---->\`)
}
_push(\`<!--p]--><span></span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
} else {
_push(\`<span><!--[p-->\`)
if (_ctx.bar2) {
_push(\`<!---->\`)
}
_push(\`<!--p]--><span></span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
}
_push(\`<!--p]--><span></span></div>\`"
`)
_push(\`<!---->\`)
}
_push(\`<!--p]--><span\${_scopeId}></span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
} else {
_push(\`<span\${_scopeId}><!--[p-->\`)
if (_ctx.bar2) {
_push(\`<!---->\`)
}
_push(\`<!--p]--><span\${_scopeId}></span></span>\`)
- _push(\`<!--if-->\`)
+ _push(\`<!--if--><!--if-->\`)
}
_push(\`<!--p]--><span\${_scopeId}></span></div>\`)
} else {
})
})
-describe.todo('block anchors', () => {
- test('if', () => {})
+describe('block anchors', () => {
+ test('if', () => {
+ expect(
+ getCompiledString(
+ `<span v-if="count === 1">1</span>
+ <span v-else-if="count === 2">2</span>
+ <span v-else-if="count === 3">3</span>
+ <span v-else>4</span>`,
+ {
+ vapor: true,
+ },
+ ),
+ ).toMatchInlineSnapshot(`
+ "\`<!--[a-->\`)
+ if (_ctx.count === 1) {
+ _push(\`<span>1</span>\`)
+ _push(\`<!--if-->\`)
+ } else if (_ctx.count === 2) {
+ _push(\`<span>2</span>\`)
+ _push(\`<!--if--><!--if-->\`)
+ } else if (_ctx.count === 3) {
+ _push(\`<span>3</span>\`)
+ _push(\`<!--if--><!--if--><!--if-->\`)
+ } else {
+ _push(\`<span>4</span>\`)
+ _push(\`<!--if--><!--if--><!--if-->\`)
+ }
+ _push(\`<!--a]-->\`"
+ `)
+ })
test('if in ssr slot vnode fallback', () => {})
test('slot in ssr slot vnode fallback', () => {})
+ test('forwarded slot', () => {})
+
test('dynamic component', () => {})
test('dynamic in ssr slot vnode fallback', () => {})
)
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]
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)),
+ )
+ }
}
}
(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`, [`\`<!--${IF_ANCHOR_LABEL}-->\``]),
- )
- }
+ return processChildrenAsStatement(branch, context, needFragmentWrapper)
+}
- return statement
+function createIfAnchors(count: number): string[] {
+ const anchors: string[] = []
+ for (let i = 0; i < count; i++) {
+ anchors.push(`<!--${IF_ANCHOR_LABEL}-->`)
+ }
+ return [`\`${anchors.join('')}\``]
}
})
n14.$key = 14
return n14
- }, null, true))
+ }))
return [n0, n3, n7]
}
}, true)
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)))
}, () => {
const n7 = t2()
return n7
- }, null, true))
+ }))
return n0
}"
`;
}, () => _createIf(() => (_ctx.orNot), () => {
const n4 = t1()
return n4
- }, null, null, true))
+ }))
return n0
}"
`;
positiveArg,
negativeArg,
once && 'true',
- isNested && 'true',
),
)
-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 {
b1: BlockFn,
b2?: BlockFn,
once?: boolean,
- elseIf?: boolean,
): Block {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
} 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))
}
applyTransitionLeaveHooks,
} from './components/Transition'
import type { VaporComponentInstance } from './component'
-import { ELSE_IF_ANCHOR_LABEL } from '@vue/shared'
export class VaporFragment<T extends Block = Block>
implements TransitionOptions
// 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,
+ )
}
}
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'