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

refactor: add anchors for v-else-if branches

daiwei 8 месяцев назад
Родитель
Сommit
167013eea6

+ 40 - 10
packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts

@@ -130,10 +130,10 @@ describe('insertion anchors', () => {
             _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>\`"
       `)
@@ -163,10 +163,10 @@ describe('insertion anchors', () => {
                   _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 {
@@ -233,7 +233,7 @@ describe('insertion anchors', () => {
               _push(\`<!---->\`)
             }
             _push(\`<!--p]--><span></span></span>\`)
-            _push(\`<!--if-->\`)
+            _push(\`<!--if--><!--if-->\`)
           } else {
             _push(\`<span><!--[p-->\`)
             if (_ctx.bar2) {
@@ -243,7 +243,7 @@ describe('insertion anchors', () => {
               _push(\`<!---->\`)
             }
             _push(\`<!--p]--><span></span></span>\`)
-            _push(\`<!--if-->\`)
+            _push(\`<!--if--><!--if-->\`)
           }
           _push(\`<!--p]--><span></span></div>\`"
       `)
@@ -296,7 +296,7 @@ describe('insertion anchors', () => {
                     _push(\`<!---->\`)
                   }
                   _push(\`<!--p]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if-->\`)
+                  _push(\`<!--if--><!--if-->\`)
                 } else {
                   _push(\`<span\${_scopeId}><!--[p-->\`)
                   if (_ctx.bar2) {
@@ -306,7 +306,7 @@ describe('insertion anchors', () => {
                     _push(\`<!---->\`)
                   }
                   _push(\`<!--p]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if-->\`)
+                  _push(\`<!--if--><!--if-->\`)
                 }
                 _push(\`<!--p]--><span\${_scopeId}></span></div>\`)
               } else {
@@ -500,8 +500,36 @@ describe('insertion anchors', () => {
   })
 })
 
-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', () => {})
 
@@ -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', () => {})

+ 30 - 12
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`, [`\`<!--${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('')}\``]
 }

+ 1 - 1
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)

+ 3 - 3
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
 }"
 `;

+ 0 - 1
packages/compiler-vapor/src/generators/if.ts

@@ -38,7 +38,6 @@ export function genIf(
       positiveArg,
       negativeArg,
       once && 'true',
-      isNested && 'true',
     ),
   )
 

+ 2 - 5
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))
   }

+ 17 - 27
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<T extends Block = Block>
   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,
+        )
       }
     }
 

+ 0 - 1
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'