edison 7 месяцев назад
Родитель
Сommit
7c45a30950
38 измененных файлов с 467 добавлено и 1848 удалено
  1. 0 5
      packages/compiler-core/src/ast.ts
  2. 0 2
      packages/compiler-core/src/codegen.ts
  3. 0 5
      packages/compiler-core/src/options.ts
  4. 0 2
      packages/compiler-core/src/transform.ts
  5. 3 10
      packages/compiler-core/src/transforms/vSlot.ts
  6. 0 1
      packages/compiler-sfc/src/compileTemplate.ts
  7. 0 828
      packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts
  8. 3 93
      packages/compiler-ssr/src/ssrCodegenTransform.ts
  9. 3 233
      packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
  10. 0 6
      packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts
  11. 2 9
      packages/compiler-ssr/src/transforms/ssrVFor.ts
  12. 1 34
      packages/compiler-ssr/src/transforms/ssrVIf.ts
  13. 4 28
      packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap
  14. 0 17
      packages/compiler-vapor/__tests__/compile.spec.ts
  15. 1 1
      packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
  16. 1 1
      packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
  17. 0 1
      packages/compiler-vapor/src/generate.ts
  18. 1 1
      packages/compiler-vapor/src/generators/block.ts
  19. 7 3
      packages/compiler-vapor/src/generators/operation.ts
  20. 7 8
      packages/compiler-vapor/src/generators/template.ts
  21. 4 0
      packages/compiler-vapor/src/ir/index.ts
  22. 8 14
      packages/compiler-vapor/src/transforms/transformChildren.ts
  23. 143 376
      packages/runtime-vapor/__tests__/hydration.spec.ts
  24. 1 2
      packages/runtime-vapor/src/apiCreateDynamicComponent.ts
  25. 4 7
      packages/runtime-vapor/src/apiCreateFor.ts
  26. 1 4
      packages/runtime-vapor/src/apiCreateIf.ts
  27. 2 1
      packages/runtime-vapor/src/block.ts
  28. 2 9
      packages/runtime-vapor/src/componentSlots.ts
  29. 45 44
      packages/runtime-vapor/src/dom/hydration.ts
  30. 57 35
      packages/runtime-vapor/src/dom/node.ts
  31. 1 0
      packages/runtime-vapor/src/dom/template.ts
  32. 47 12
      packages/runtime-vapor/src/fragment.ts
  33. 107 8
      packages/runtime-vapor/src/insertionState.ts
  34. 5 9
      packages/runtime-vapor/src/vdomInterop.ts
  35. 2 15
      packages/server-renderer/src/helpers/ssrRenderSlot.ts
  36. 5 11
      packages/server-renderer/src/render.ts
  37. 0 12
      packages/shared/src/domAnchors.ts
  38. 0 1
      packages/shared/src/index.ts

+ 0 - 5
packages/compiler-core/src/ast.ts

@@ -163,7 +163,6 @@ export interface ComponentNode extends BaseElementNode {
     | MemoExpression // when cached by v-memo
     | undefined
   ssrCodegenNode?: CallExpression
-  needAnchor?: boolean
 }
 
 export interface SlotOutletNode extends BaseElementNode {
@@ -173,14 +172,12 @@ export interface SlotOutletNode extends BaseElementNode {
     | CacheExpression // when cached by v-once
     | undefined
   ssrCodegenNode?: CallExpression
-  needAnchor?: boolean
 }
 
 export interface TemplateNode extends BaseElementNode {
   tagType: ElementTypes.TEMPLATE
   // TemplateNode is a container type that always gets compiled away
   codegenNode: undefined
-  needAnchor?: boolean
 }
 
 export interface TextNode extends Node {
@@ -290,7 +287,6 @@ export interface IfNode extends Node {
   type: NodeTypes.IF
   branches: IfBranchNode[]
   codegenNode?: IfConditionalExpression | CacheExpression // <div v-if v-once>
-  needAnchor?: boolean
 }
 
 export interface IfBranchNode extends Node {
@@ -310,7 +306,6 @@ export interface ForNode extends Node {
   parseResult: ForParseResult
   children: TemplateChildNode[]
   codegenNode?: ForCodegenNode
-  needAnchor?: boolean
 }
 
 export interface ForParseResult {

+ 0 - 2
packages/compiler-core/src/codegen.ts

@@ -167,7 +167,6 @@ function createCodegenContext(
     ssr = false,
     isTS = false,
     inSSR = false,
-    vapor = false,
   }: CodegenOptions,
 ): CodegenContext {
   const context: CodegenContext = {
@@ -183,7 +182,6 @@ function createCodegenContext(
     ssr,
     isTS,
     inSSR,
-    vapor,
     source: ast.source,
     code: ``,
     column: 1,

+ 0 - 5
packages/compiler-core/src/options.ts

@@ -220,11 +220,6 @@ interface SharedTransformCodegenOptions {
    * @default 'template.vue.html'
    */
   filename?: string
-
-  /**
-   * Indicates vapor component
-   */
-  vapor?: boolean
 }
 
 export interface TransformOptions

+ 0 - 2
packages/compiler-core/src/transform.ts

@@ -146,7 +146,6 @@ export function createTransformContext(
     slotted = true,
     ssr = false,
     inSSR = false,
-    vapor = false,
     ssrCssVars = ``,
     bindingMetadata = EMPTY_OBJ,
     inline = false,
@@ -174,7 +173,6 @@ export function createTransformContext(
     slotted,
     ssr,
     inSSR,
-    vapor,
     ssrCssVars,
     bindingMetadata,
     inline,

+ 3 - 10
packages/compiler-core/src/transforms/vSlot.ts

@@ -100,7 +100,6 @@ export type SlotFnBuilder = (
   vFor: DirectiveNode | undefined,
   slotChildren: TemplateChildNode[],
   loc: SourceLocation,
-  parent: ElementNode,
 ) => FunctionExpression
 
 const buildClientSlotFn: SlotFnBuilder = (props, _vForExp, children, loc) =>
@@ -148,7 +147,7 @@ export function buildSlots(
     slotsProperties.push(
       createObjectProperty(
         arg || createSimpleExpression('default', true),
-        buildSlotFn(exp, undefined, children, loc, node),
+        buildSlotFn(exp, undefined, children, loc),
       ),
     )
   }
@@ -201,13 +200,7 @@ export function buildSlots(
     }
 
     const vFor = findDir(slotElement, 'for')
-    const slotFunction = buildSlotFn(
-      slotProps,
-      vFor,
-      slotChildren,
-      slotLoc,
-      slotElement,
-    )
+    const slotFunction = buildSlotFn(slotProps, vFor, slotChildren, slotLoc)
 
     // check if this slot is conditional (v-if/v-for)
     let vIf: DirectiveNode | undefined
@@ -311,7 +304,7 @@ export function buildSlots(
       props: ExpressionNode | undefined,
       children: TemplateChildNode[],
     ) => {
-      const fn = buildSlotFn(props, undefined, children, loc, node)
+      const fn = buildSlotFn(props, undefined, children, loc)
       if (__COMPAT__ && context.compatConfig) {
         fn.isNonScopedSlot = true
       }

+ 0 - 1
packages/compiler-sfc/src/compileTemplate.ts

@@ -253,7 +253,6 @@ function doCompileTemplate({
       slotted,
       sourceMap: true,
       ...compilerOptions,
-      vapor,
       hmr: !isProd,
       nodeTransforms: nodeTransforms.concat(
         compilerOptions.nodeTransforms || [],

+ 0 - 828
packages/compiler-ssr/__tests__/ssrVaporAnchors.spec.ts

@@ -1,828 +0,0 @@
-import { getCompiledString } from './utils'
-
-describe('block anchors', () => {
-  describe('prepend', () => {
-    test('prepend anchor with component', () => {
-      expect(
-        getCompiledString('<div><Comp/><Comp/><span/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with component in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <Comp/><Comp/><span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-                _push(\`<!--]]--><!--[[-->\`)
-                _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    _createVNode(_component_Comp),
-                    _createCommentVNode("]]"),
-                    _createCommentVNode("[["),
-                    _createVNode(_component_Comp),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with slot', () => {
-      expect(
-        getCompiledString('<div><slot name="foo"/><slot/><span/></div>', {
-          vapor: true,
-        }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent)
-          _push(\`<!--slot--><!--]]--><!--[[-->\`)
-          _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent)
-          _push(\`<!--slot--><!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with slot in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <slot name="foo"/>
-              <slot/>
-              <span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent, _scopeId)
-                _push(\`<!--slot--><!--]]--><!--[[-->\`)
-                _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-                _push(\`<!--slot--><!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    _renderSlot(_ctx.$slots, "foo"),
-                    _createCommentVNode("slot"),
-                    _createCommentVNode("]]"),
-                    _createCommentVNode("[["),
-                    _renderSlot(_ctx.$slots, "default"),
-                    _createCommentVNode("slot"),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 3 /* FORWARDED */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with v-if/else-if/else', () => {
-      expect(
-        getCompiledString(
-          `<div>
-            <span v-if="foo"/>
-            <span v-else-if="bar"/>
-            <span v-else/>
-            <span/>
-          </div>`,
-          {
-            vapor: true,
-          },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          if (_ctx.foo) {
-            _push(\`<span></span>\`)
-            _push(\`<!--if-->\`)
-          } else if (_ctx.bar) {
-            _push(\`<span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          } else {
-            _push(\`<span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          }
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with v-if/else-if/else in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-if="foo"/>
-              <span v-else-if="bar"/>
-              <span v-else/>
-              <span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                if (_ctx.foo) {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if-->\`)
-                } else if (_ctx.bar) {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                } else {
-                  _push(\`<span\${_scopeId}></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                }
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_ctx.foo)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                          _createVNode("span"),
-                          _createCommentVNode("<!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_ctx.bar)
-                        ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                            _createVNode("span"),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */))
-                        : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                            _createVNode("span"),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */)),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with nested v-if', () => {
-      expect(
-        getCompiledString(
-          `<div>
-            <span v-if="foo">
-              <span v-if="foo1" />
-              <span />
-            </span>
-            <span v-else-if="bar">
-              <span v-if="bar1" />
-              <span />
-            </span>
-            <span v-else>
-              <span v-if="bar2" />
-              <span />
-            </span>
-            <span />
-          </div>`,
-          {
-            vapor: true,
-          },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          if (_ctx.foo) {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.foo1) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if-->\`)
-          } else if (_ctx.bar) {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.bar1) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          } else {
-            _push(\`<span><!--[[-->\`)
-            if (_ctx.bar2) {
-              _push(\`<span></span>\`)
-              _push(\`<!--if-->\`)
-            } else {
-              _push(\`<!--if-->\`)
-            }
-            _push(\`<!--]]--><span></span></span>\`)
-            _push(\`<!--if--><!--if-->\`)
-          }
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with nested v-if in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-if="foo">
-                <span v-if="foo1" />
-                <span />
-              </span>
-              <span v-else-if="bar">
-                <span v-if="bar1" />
-                <span />
-              </span>
-              <span v-else>
-                <span v-if="bar2" />
-                <span />
-              </span>
-              <span />
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                if (_ctx.foo) {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.foo1) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if-->\`)
-                } else if (_ctx.bar) {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.bar1) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                } else {
-                  _push(\`<span\${_scopeId}><!--[[-->\`)
-                  if (_ctx.bar2) {
-                    _push(\`<span\${_scopeId}></span>\`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><span\${_scopeId}></span></span>\`)
-                  _push(\`<!--if--><!--if-->\`)
-                }
-                _push(\`<!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_ctx.foo)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                          _createVNode("span", null, [
-                            _createCommentVNode("[["),
-                            (_ctx.foo1)
-                              ? (_openBlock(), _createBlock("span", { key: 0 }))
-                              : _createCommentVNode("v-if", true),
-                            _createCommentVNode("if"),
-                            _createCommentVNode("]]"),
-                            _createVNode("span")
-                          ]),
-                          _createCommentVNode("<!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_ctx.bar)
-                        ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                            _createVNode("span", null, [
-                              _createCommentVNode("[["),
-                              (_ctx.bar1)
-                                ? (_openBlock(), _createBlock("span", { key: 0 }))
-                                : _createCommentVNode("v-if", true),
-                              _createCommentVNode("if"),
-                              _createCommentVNode("]]"),
-                              _createVNode("span")
-                            ]),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */))
-                        : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                            _createVNode("span", null, [
-                              _createCommentVNode("[["),
-                              (_ctx.bar2)
-                                ? (_openBlock(), _createBlock("span", { key: 0 }))
-                                : _createCommentVNode("v-if", true),
-                              _createCommentVNode("if"),
-                              _createCommentVNode("]]"),
-                              _createVNode("span")
-                            ]),
-                            _createCommentVNode("<!--if--><!--if-->")
-                          ], 64 /* STABLE_FRAGMENT */)),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with template v-if', () => {
-      expect(
-        getCompiledString(
-          `<component :is="tag">
-            <div v-if="foo">
-              <template v-if="depth < 5">
-                foo
-              </template>
-              <div></div>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                if (_ctx.foo) {
-                  _push(\`<div\${_scopeId}><!--[[-->\`)
-                  if (_ctx.depth < 5) {
-                    _push(\` foo \`)
-                    _push(\`<!--if-->\`)
-                  } else {
-                    _push(\`<!--if-->\`)
-                  }
-                  _push(\`<!--]]--><div\${_scopeId}></div></div>\`)
-                  _push(\`<!--if-->\`)
-                } else {
-                  _push(\`<!--if-->\`)
-                }
-              } else {
-                return [
-                  (_ctx.foo)
-                    ? (_openBlock(), _createBlock("div", { key: 0 }, [
-                        _createCommentVNode("[["),
-                        (_ctx.depth < 5)
-                          ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                              _createTextVNode(" foo ")
-                            ], 64 /* STABLE_FRAGMENT */))
-                          : _createCommentVNode("v-if", true),
-                        _createCommentVNode("if"),
-                        _createCommentVNode("]]"),
-                        _createVNode("div")
-                      ]))
-                    : _createCommentVNode("v-if", true),
-                  _createCommentVNode("if")
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-
-    test('prepend anchor with v-for', () => {
-      expect(
-        getCompiledString('<div><span v-for="item in items"/><span/></div>', {
-          vapor: true,
-        }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><!--[[-->\`)
-          _ssrRenderList(_ctx.items, (item) => {
-            _push(\`<span></span>\`)
-          })
-          _push(\`<!--for--><!--]]--><span></span></div>\`"
-      `)
-    })
-
-    test('prepend anchor with v-for in ssr slot vnode fallback', () => {
-      expect(
-        getCompiledString(
-          `<component :is="'div'">
-            <div>
-              <span v-for="item in items"/><span/>
-            </div>
-          </component>`,
-          { vapor: true },
-        ),
-      ).toMatchInlineSnapshot(`
-        "\`\`)
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<div\${_scopeId}><!--[[-->\`)
-                _ssrRenderList(_ctx.items, (item) => {
-                  _push(\`<span\${_scopeId}></span>\`)
-                })
-                _push(\`<!--for--><!--]]--><span\${_scopeId}></span></div>\`)
-              } else {
-                return [
-                  _createVNode("div", null, [
-                    _createCommentVNode("[["),
-                    (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
-                      return (_openBlock(), _createBlock("span"))
-                    }), 256 /* UNKEYED_FRAGMENT */)),
-                    _createCommentVNode("for"),
-                    _createCommentVNode("]]"),
-                    _createVNode("span")
-                  ])
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }), _parent)
-          _push(\`<!--dynamic-component-->\`"
-      `)
-    })
-  })
-
-  // TODO add more tests
-  describe('insert', () => {
-    test('insertion anchor with component', () => {
-      expect(
-        getCompiledString('<div><span/><Comp/><span/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><span></span><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><span></span></div>\`"
-      `)
-    })
-  })
-
-  // TODO add more tests
-  describe('append', () => {
-    test('append anchor', () => {
-      expect(
-        getCompiledString('<div><span/><Comp/><Comp/></div>', { vapor: true }),
-      ).toMatchInlineSnapshot(`
-        "\`<div><span></span><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--><!--[[-->\`)
-          _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-          _push(\`<!--]]--></div>\`"
-      `)
-    })
-  })
-
-  test('mixed anchors', () => {
-    expect(
-      getCompiledString('<div><Comp/><span/><Comp/><span/><Comp/></div>', {
-        vapor: true,
-      }),
-    ).toMatchInlineSnapshot(`
-      "\`<div><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--><span></span><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--><span></span><!--[[-->\`)
-        _push(_ssrRenderComponent(_component_Comp, null, null, _parent))
-        _push(\`<!--]]--></div>\`"
-    `)
-  })
-
-  test('mixed anchors in ssr slot vnode fallback', () => {
-    expect(
-      getCompiledString(
-        `<component :is="'div'">
-          <div>
-            <Comp/><span/>
-            <Comp/><span/>
-            <Comp/>
-          </div>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent('div'), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _push(\`<div\${_scopeId}><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--><span\${_scopeId}></span><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--><span\${_scopeId}></span><!--[[-->\`)
-              _push(_ssrRenderComponent(_component_Comp, null, null, _parent, _scopeId))
-              _push(\`<!--]]--></div>\`)
-            } else {
-              return [
-                _createVNode("div", null, [
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]"),
-                  _createVNode("span"),
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]"),
-                  _createVNode("span"),
-                  _createCommentVNode("[["),
-                  _createVNode(_component_Comp),
-                  _createCommentVNode("]]")
-                ])
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-})
-
-describe('fragment anchors', () => {
-  test('if', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <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>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              if (_ctx.count === 1) {
-                _push(\`<span\${_scopeId}>1</span>\`)
-                _push(\`<!--if-->\`)
-              } else if (_ctx.count === 2) {
-                _push(\`<span\${_scopeId}>2</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              } else if (_ctx.count === 3) {
-                _push(\`<span\${_scopeId}>3</span>\`)
-                _push(\`<!--if--><!--if--><!--if-->\`)
-              } else {
-                _push(\`<span\${_scopeId}>4</span>\`)
-                _push(\`<!--if--><!--if--><!--if-->\`)
-              }
-            } else {
-              return [
-                (_ctx.count === 1)
-                  ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                      _createVNode("span", null, "1"),
-                      _createCommentVNode("<!--if-->")
-                    ], 64 /* STABLE_FRAGMENT */))
-                  : (_ctx.count === 2)
-                    ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                        _createVNode("span", null, "2"),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-                    : (_ctx.count === 3)
-                      ? (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                          _createVNode("span", null, "3"),
-                          _createCommentVNode("<!--if--><!--if--><!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-                      : (_openBlock(), _createBlock(_Fragment, { key: 3 }, [
-                          _createVNode("span", null, "4"),
-                          _createCommentVNode("<!--if--><!--if--><!--if-->")
-                        ], 64 /* STABLE_FRAGMENT */))
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('if + v-html/v-text', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <span v-if="count === 1" v-html="html"></span>
-          <span v-else-if="count === 2" v-text="txt"></span>
-          <span v-else>4</span>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              if (_ctx.count === 1) {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  (_ctx.html) ?? ''
-                }</span>\`)
-                _push(\`<!--if-->\`)
-              } else if (_ctx.count === 2) {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  _ssrInterpolate(_ctx.txt)
-                }</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              } else {
-                _push(\`<span\${_scopeId}>4</span>\`)
-                _push(\`<!--if--><!--if-->\`)
-              }
-            } else {
-              return [
-                (_ctx.count === 1)
-                  ? (_openBlock(), _createBlock(_Fragment, { key: 0 }, [
-                      _createVNode("span", { innerHTML: _ctx.html }, null, 8 /* PROPS */, ["innerHTML"]),
-                      _createCommentVNode("<!--if-->")
-                    ], 64 /* STABLE_FRAGMENT */))
-                  : (_ctx.count === 2)
-                    ? (_openBlock(), _createBlock(_Fragment, { key: 1 }, [
-                        _createVNode("span", {
-                          textContent: _toDisplayString(_ctx.txt)
-                        }, null, 8 /* PROPS */, ["textContent"]),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-                    : (_openBlock(), _createBlock(_Fragment, { key: 2 }, [
-                        _createVNode("span", null, "4"),
-                        _createCommentVNode("<!--if--><!--if-->")
-                      ], 64 /* STABLE_FRAGMENT */))
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('for', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <span v-for="item in items">{{item}}</span>
-        </component>`,
-        {
-          vapor: true,
-        },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _ssrRenderList(_ctx.items, (item) => {
-                _push(\`<span\${
-                  _scopeId
-                }>\${
-                  _ssrInterpolate(item)
-                }</span>\`)
-              })
-              _push(\`<!--for-->\`)
-            } else {
-              return [
-                (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.items, (item) => {
-                  return (_openBlock(), _createBlock("span", null, _toDisplayString(item), 1 /* TEXT */))
-                }), 256 /* UNKEYED_FRAGMENT */)),
-                _createCommentVNode("for")
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('slot', () => {
-    expect(
-      getCompiledString(
-        `<div>
-          <slot name="foo"/>
-          <slot/>
-        </div>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`<div><!--[[-->\`)
-        _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent)
-        _push(\`<!--slot--><!--]]--><!--[[-->\`)
-        _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent)
-        _push(\`<!--slot--><!--]]--></div>\`"
-    `)
-  })
-
-  test('forwarded slot', () => {
-    expect(
-      getCompiledString(
-        `<component :is="tag">
-          <slot name="foo"/>
-          <slot/>
-        </component>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.tag), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _ssrRenderSlot(_ctx.$slots, "foo", {}, null, _push, _parent, _scopeId)
-              _push(\`<!--slot-->\`)
-              _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-              _push(\`<!--slot-->\`)
-            } else {
-              return [
-                _renderSlot(_ctx.$slots, "foo"),
-                _createCommentVNode("slot"),
-                _renderSlot(_ctx.$slots, "default"),
-                _createCommentVNode("slot")
-              ]
-            }
-          }),
-          _: 3 /* FORWARDED */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-
-  test('dynamic component', () => {
-    expect(
-      getCompiledString(
-        `<component is='tag'>
-          <div>
-            <component is="foo"/>
-          </div>
-        </component>`,
-        { vapor: true },
-      ),
-    ).toMatchInlineSnapshot(`
-      "\`\`)
-        _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("tag"), null, {
-          default: _withCtx((_, _push, _parent, _scopeId) => {
-            if (_push) {
-              _push(\`<div\${_scopeId}>\`)
-              _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("foo"), null, null), _parent, _scopeId)
-              _push(\`<!--dynamic-component--></div>\`)
-            } else {
-              return [
-                _createVNode("div", null, [
-                  (_openBlock(), _createBlock(_resolveDynamicComponent("foo"))),
-                  _createCommentVNode("dynamic-component")
-                ])
-              ]
-            }
-          }),
-          _: 1 /* STABLE */
-        }), _parent)
-        _push(\`<!--dynamic-component-->\`"
-    `)
-  })
-})

+ 3 - 93
packages/compiler-ssr/src/ssrCodegenTransform.ts

@@ -1,16 +1,13 @@
 import {
-  type AttributeNode,
   type BlockStatement,
   type CallExpression,
   type CompilerError,
   type CompilerOptions,
-  type DirectiveNode,
   type ElementNode,
   ElementTypes,
   type IfStatement,
   type JSChildNode,
   NodeTypes,
-  type PlainElementNode,
   type RootNode,
   type TemplateChildNode,
   type TemplateLiteral,
@@ -24,12 +21,7 @@ import {
   isText,
   processExpression,
 } from '@vue/compiler-dom'
-import {
-  BLOCK_ANCHOR_END_LABEL,
-  BLOCK_ANCHOR_START_LABEL,
-  escapeHtml,
-  isString,
-} from '@vue/shared'
+import { escapeHtml, isString } from '@vue/shared'
 import { SSR_INTERPOLATE, ssrHelpers } from './runtimeHelpers'
 import { ssrProcessIf } from './transforms/ssrVIf'
 import { ssrProcessFor } from './transforms/ssrVFor'
@@ -167,17 +159,12 @@ export function processChildren(
   disableNestedFragments = false,
   disableComment = false,
 ): void {
-  const vapor = context.options.vapor
-  if (asFragment && !vapor) {
+  if (asFragment) {
     context.pushStringPart(`<!--[-->`)
   }
 
   const { children } = parent
 
-  if (vapor && isElementWithChildren(parent as PlainElementNode)) {
-    processBlockNodeAnchor(children)
-  }
-
   for (let i = 0; i < children.length; i++) {
     const child = children[i]
     switch (child.type) {
@@ -187,19 +174,11 @@ export function processChildren(
             ssrProcessElement(child, context)
             break
           case ElementTypes.COMPONENT:
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
             ssrProcessComponent(child, context, parent)
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
 
             break
           case ElementTypes.SLOT:
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
             ssrProcessSlotOutlet(child, context)
-            if (child.needAnchor)
-              context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
             break
           case ElementTypes.TEMPLATE:
             // TODO
@@ -234,18 +213,10 @@ export function processChildren(
         )
         break
       case NodeTypes.IF:
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
         ssrProcessIf(child, context, disableNestedFragments, disableComment)
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
         break
       case NodeTypes.FOR:
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_START_LABEL}-->`)
         ssrProcessFor(child, context, disableNestedFragments)
-        if (child.needAnchor)
-          context.pushStringPart(`<!--${BLOCK_ANCHOR_END_LABEL}-->`)
         break
       case NodeTypes.IF_BRANCH:
         // no-op - handled by ssrProcessIf
@@ -267,7 +238,7 @@ export function processChildren(
         return exhaustiveCheck
     }
   }
-  if (asFragment && !vapor) {
+  if (asFragment) {
     context.pushStringPart(`<!--]-->`)
   }
 }
@@ -283,67 +254,6 @@ export function processChildrenAsStatement(
   return createBlockStatement(childContext.body)
 }
 
-export function processBlockNodeAnchor(children: TemplateChildNode[]): void {
-  let prevBlocks: (TemplateChildNode & { needAnchor?: boolean })[] = []
-  let hasStaticNode = false
-  let blockCount = 0
-  for (const child of children) {
-    if (isBlockNode(child)) {
-      prevBlocks.push(child)
-      blockCount++
-    }
-
-    if (isStaticNode(child)) {
-      if (prevBlocks.length) {
-        if (hasStaticNode) {
-          // insert
-          prevBlocks.forEach(child => (child.needAnchor = true))
-        } else {
-          // prepend
-          prevBlocks.forEach(child => (child.needAnchor = true))
-        }
-        prevBlocks = []
-      }
-      hasStaticNode = true
-    }
-  }
-
-  // When there is only one block node, no anchor is needed,
-  // firstChild is used as the hydration node
-  if (prevBlocks.length && !(blockCount === 1 && !hasStaticNode)) {
-    // append
-    prevBlocks.forEach(child => (child.needAnchor = true))
-  }
-}
-
-export function hasBlockDir(
-  props: Array<AttributeNode | DirectiveNode>,
-): boolean {
-  return props.some(p => p.name === 'if' || p.name === 'for')
-}
-
-function isBlockNode(child: TemplateChildNode): boolean {
-  return (
-    child.type === NodeTypes.IF ||
-    child.type === NodeTypes.FOR ||
-    (child.type === NodeTypes.ELEMENT &&
-      (child.tagType === ElementTypes.COMPONENT ||
-        child.tagType === ElementTypes.SLOT ||
-        hasBlockDir(child.props)))
-  )
-}
-
-function isStaticNode(child: TemplateChildNode): boolean {
-  return (
-    child.type === NodeTypes.TEXT ||
-    child.type === NodeTypes.INTERPOLATION ||
-    child.type === NodeTypes.COMMENT ||
-    (child.type === NodeTypes.ELEMENT &&
-      child.tagType === ElementTypes.ELEMENT &&
-      !hasBlockDir(child.props))
-  )
-}
-
 export function isElementWithChildren(
   node: TemplateChildNode,
 ): node is ElementNode {

+ 3 - 233
packages/compiler-ssr/src/transforms/ssrTransformComponent.ts

@@ -1,7 +1,6 @@
 import {
   CREATE_VNODE,
   type CallExpression,
-  type CommentNode,
   type CompilerOptions,
   type ComponentNode,
   DOMDirectiveTransforms,
@@ -9,14 +8,11 @@ import {
   type DirectiveNode,
   ElementTypes,
   type ExpressionNode,
-  type ForNode,
   type FunctionExpression,
-  type IfNode,
   type JSChildNode,
   Namespaces,
   type NodeTransform,
   NodeTypes,
-  type PlainElementNode,
   RESOLVE_DYNAMIC_COMPONENT,
   type ReturnStatement,
   type RootNode,
@@ -47,8 +43,6 @@ import {
 import { SSR_RENDER_COMPONENT, SSR_RENDER_VNODE } from '../runtimeHelpers'
 import {
   type SSRTransformContext,
-  isElementWithChildren,
-  processBlockNodeAnchor,
   processChildren,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
@@ -61,19 +55,7 @@ import {
   ssrProcessTransitionGroup,
   ssrTransformTransitionGroup,
 } from './ssrTransformTransitionGroup'
-import {
-  BLOCK_ANCHOR_END_LABEL,
-  BLOCK_ANCHOR_START_LABEL,
-  DYNAMIC_COMPONENT_ANCHOR_LABEL,
-  FOR_ANCHOR_LABEL,
-  IF_ANCHOR_LABEL,
-  SLOT_ANCHOR_LABEL,
-  extend,
-  isArray,
-  isObject,
-  isPlainObject,
-  isSymbol,
-} from '@vue/shared'
+import { extend, isArray, isObject, isPlainObject, isSymbol } from '@vue/shared'
 import { buildSSRProps } from './ssrTransformElement'
 import {
   ssrProcessTransition,
@@ -144,9 +126,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
     // fallback in case the child is render-fn based). Store them in an array
     // for later use.
     if (clonedNode.children.length) {
-      buildSlots(clonedNode, context, (props, vFor, children, _loc, parent) => {
+      buildSlots(clonedNode, context, (props, vFor, children) => {
         vnodeBranches.push(
-          createVNodeSlotBranch(props, vFor, children, context, parent),
+          createVNodeSlotBranch(props, vFor, children, context),
         )
         return createFunctionExpression(undefined)
       })
@@ -282,11 +264,6 @@ export function ssrProcessComponent(
       // dynamic component (`resolveDynamicComponent` call)
       // the codegen node is a `renderVNode` call
       context.pushStatement(node.ssrCodegenNode)
-
-      // anchor for vapor dynamic component
-      if (context.options.vapor) {
-        context.pushStringPart(`<!--${DYNAMIC_COMPONENT_ANCHOR_LABEL}-->`)
-      }
     }
   }
 }
@@ -309,7 +286,6 @@ function createVNodeSlotBranch(
   vFor: DirectiveNode | undefined,
   children: TemplateChildNode[],
   parentContext: TransformContext,
-  parent: TemplateChildNode,
 ): ReturnStatement {
   // apply a sub-transform using vnode-based transforms.
   const rawOptions = rawOptionsMap.get(parentContext.root)!
@@ -344,9 +320,6 @@ function createVNodeSlotBranch(
   if (vFor) {
     wrapperProps.push(extend({}, vFor))
   }
-  if (parentContext.vapor) {
-    children = injectVaporAnchors(children, parent)
-  }
 
   const wrapperNode: TemplateNode = {
     type: NodeTypes.ELEMENT,
@@ -400,209 +373,6 @@ function subTransform(
   // - hoists are not enabled for the client branch here
 }
 
-function injectVaporAnchors(
-  children: TemplateChildNode[],
-  parent: TemplateChildNode,
-): TemplateChildNode[] {
-  if (isElementWithChildren(parent)) {
-    processBlockNodeAnchor(children)
-  }
-
-  const newChildren: TemplateChildNode[] = []
-  for (let i = 0; i < children.length; i++) {
-    const child = children[i]
-
-    if (child.type !== NodeTypes.ELEMENT) {
-      newChildren.push(child)
-      continue
-    }
-
-    const { tagType, props } = child
-    let needBlockAnchor: boolean | undefined
-
-    if (
-      tagType === ElementTypes.COMPONENT ||
-      tagType === ElementTypes.SLOT ||
-      tagType === ElementTypes.TEMPLATE
-    ) {
-      needBlockAnchor = child.needAnchor
-    } else if (tagType === ElementTypes.ELEMENT) {
-      let hasIf = false
-      let hasFor = false
-
-      for (const prop of props) {
-        if (prop.name === 'if') {
-          hasIf = true
-          break
-        } else if (prop.name === 'for') {
-          hasFor = true
-        }
-      }
-
-      if (hasIf) {
-        needBlockAnchor = (child as any as IfNode).needAnchor
-        const lastBranchIndex = findLastIfBranchIndex(children, i)
-        if (lastBranchIndex > i) {
-          injectIfAnchors(
-            needBlockAnchor,
-            newChildren,
-            i,
-            lastBranchIndex,
-            children,
-          )
-          i = lastBranchIndex
-          continue
-        }
-      } else if (hasFor) {
-        needBlockAnchor = (child as any as ForNode).needAnchor
-      }
-    }
-
-    if (needBlockAnchor) {
-      newChildren.push(createAnchor(BLOCK_ANCHOR_START_LABEL))
-    }
-
-    newChildren.push(child)
-
-    // inject fragment anchor
-    const fragmentAnchorLabel = getFragmentAnchorLabel(child)
-    if (fragmentAnchorLabel) newChildren.push(createAnchor(fragmentAnchorLabel))
-
-    if (needBlockAnchor) {
-      newChildren.push(createAnchor(BLOCK_ANCHOR_END_LABEL))
-    }
-
-    child.children = injectVaporAnchors(child.children, child)
-  }
-
-  return newChildren
-}
-
-function injectIfAnchors(
-  needBlockAnchor: boolean | undefined,
-  newChildren: TemplateChildNode[],
-  i: number,
-  lastBranchIndex: number,
-  children: TemplateChildNode[],
-) {
-  if (needBlockAnchor) {
-    newChildren.push(createAnchor(BLOCK_ANCHOR_START_LABEL))
-  }
-
-  for (let j = i; j <= lastBranchIndex; j++) {
-    const node = children[j] as PlainElementNode
-    const fragmentAnchorLabel = getFragmentAnchorLabel(node)
-    let isElse = false
-
-    const conditionalProps: typeof node.props = []
-    const restProps: typeof node.props = []
-
-    for (const prop of node.props) {
-      if (
-        prop.name === 'if' ||
-        prop.name === 'else-if' ||
-        prop.name === 'else'
-      ) {
-        conditionalProps.push(prop)
-        if (prop.name === 'else') isElse = true
-      } else {
-        restProps.push(prop)
-      }
-    }
-    node.props = restProps
-
-    // wrap the node with a template node
-    const wrapperNode: TemplateNode = {
-      type: NodeTypes.ELEMENT,
-      ns: Namespaces.HTML,
-      tag: 'template',
-      tagType: ElementTypes.TEMPLATE,
-      props: conditionalProps,
-      children: [node],
-      loc: node.loc,
-      codegenNode: undefined,
-    }
-    newChildren.push(wrapperNode)
-
-    if (fragmentAnchorLabel) {
-      const repeatCount = j - i - (isElse ? 1 : 0) + 1
-      wrapperNode.children.push(
-        createAnchor(`<!--${fragmentAnchorLabel}-->`.repeat(repeatCount)),
-      )
-    }
-    node.children = injectVaporAnchors(node.children, node)
-  }
-
-  if (needBlockAnchor) {
-    newChildren.push(createAnchor(BLOCK_ANCHOR_END_LABEL))
-  }
-}
-
-function createAnchor(content: string): CommentNode {
-  return {
-    type: NodeTypes.COMMENT,
-    content,
-    loc: locStub,
-  }
-}
-
-function findLastIfBranchIndex(
-  children: TemplateChildNode[],
-  ifIndex: number,
-): number {
-  let lastIndex = ifIndex
-
-  for (let i = ifIndex + 1; i < children.length; i++) {
-    const sibling = children[i]
-
-    if (sibling.type !== NodeTypes.ELEMENT) {
-      continue
-    }
-
-    let hasElseIf = false
-    let hasElse = false
-
-    for (const prop of sibling.props) {
-      if (prop.name === 'else-if') {
-        hasElseIf = true
-        break
-      } else if (prop.name === 'else') {
-        hasElse = true
-        break
-      }
-    }
-
-    if (hasElseIf || hasElse) {
-      lastIndex = i
-      if (hasElse) {
-        break
-      }
-    } else {
-      break
-    }
-  }
-
-  return lastIndex
-}
-
-function getFragmentAnchorLabel(child: TemplateChildNode): string | undefined {
-  if (child.type !== NodeTypes.ELEMENT) return
-
-  if (child.tagType === ElementTypes.COMPONENT && child.tag === 'component') {
-    return DYNAMIC_COMPONENT_ANCHOR_LABEL
-  } else if (child.tagType === ElementTypes.SLOT) {
-    return SLOT_ANCHOR_LABEL
-  } else if (
-    child.props.some(
-      p => p.name === 'if' || p.name === 'else-if' || p.name === 'else',
-    )
-  ) {
-    return IF_ANCHOR_LABEL
-  } else if (child.props.some(p => p.name === 'for')) {
-    return FOR_ANCHOR_LABEL
-  }
-}
-
 function clone(v: any): any {
   if (isArray(v)) {
     return v.map(clone)

+ 0 - 6
packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts

@@ -16,7 +16,6 @@ import {
   type SSRTransformContext,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
-import { SLOT_ANCHOR_LABEL } from '@vue/shared'
 
 export const ssrTransformSlotOutlet: NodeTransform = (node, context) => {
   if (isSlotOutlet(node)) {
@@ -94,9 +93,4 @@ export function ssrProcessSlotOutlet(
   }
 
   context.pushStatement(node.ssrCodegenNode!)
-
-  // anchor for vapor slot
-  if (context.options.vapor) {
-    context.pushStringPart(`<!--${SLOT_ANCHOR_LABEL}-->`)
-  }
 }

+ 2 - 9
packages/compiler-ssr/src/transforms/ssrVFor.ts

@@ -13,7 +13,6 @@ import {
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
 import { SSR_RENDER_LIST } from '../runtimeHelpers'
-import { FOR_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformFor: NodeTransform =
@@ -38,9 +37,8 @@ export function ssrProcessFor(
     needFragmentWrapper,
   )
 
-  const vapor = context.options.vapor
   // v-for always renders a fragment unless explicitly disabled
-  if (!disableNestedFragments && !vapor) {
+  if (!disableNestedFragments) {
     context.pushStringPart(`<!--[-->`)
   }
   context.pushStatement(
@@ -49,12 +47,7 @@ export function ssrProcessFor(
       renderLoop,
     ]),
   )
-  if (!disableNestedFragments && !vapor) {
+  if (!disableNestedFragments) {
     context.pushStringPart(`<!--]-->`)
   }
-
-  // anchor for vapor v-for fragment
-  if (vapor) {
-    context.pushStringPart(`<!--${FOR_ANCHOR_LABEL}-->`)
-  }
 }

+ 1 - 34
packages/compiler-ssr/src/transforms/ssrVIf.ts

@@ -14,7 +14,6 @@ import {
   type SSRTransformContext,
   processChildrenAsStatement,
 } from '../ssrCodegenTransform'
-import { IF_ANCHOR_LABEL } from '@vue/shared'
 
 // Plugin for the first transform pass, which simply constructs the AST node
 export const ssrTransformIf: NodeTransform = createStructuralDirectiveTransform(
@@ -37,16 +36,6 @@ 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]
@@ -61,29 +50,15 @@ 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)),
-        )
-      }
     }
   }
 
   if (!currentIf.alternate && !disableComment) {
     currentIf.alternate = createBlockStatement([
-      createCallExpression(`_push`, [
-        isVapor ? `\`<!--${IF_ANCHOR_LABEL}-->\`` : '`<!---->`',
-      ]),
+      createCallExpression(`_push`, ['`<!---->`']),
     ])
   }
 }
@@ -102,11 +77,3 @@ function processIfBranch(
 
   return processChildrenAsStatement(branch, context, needFragmentWrapper)
 }
-
-function createIfAnchors(count: number): string[] {
-  const anchors: string[] = []
-  for (let i = 0; i < count; i++) {
-    anchors.push(`<!--${IF_ANCHOR_LABEL}-->`)
-  }
-  return [`\`${anchors.join('')}\``]
-}

+ 4 - 28
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap

@@ -38,7 +38,7 @@ export function render(_ctx) {
     "default": () => {
       const n0 = _createIf(() => (true), () => {
         const n3 = t0()
-        _setInsertionState(n3)
+        _setInsertionState(n3, null)
         const n2 = _createComponentWithFallback(_component_Bar)
         _withVaporDirectives(n2, [[_directive_hello, void 0, void 0, { world: true }]])
         return n3
@@ -157,9 +157,9 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
   const _component_Comp = _resolveComponent("Comp")
   const n0 = t0()
   const n3 = t1()
-  const n2 = _child(n3)
   _setInsertionState(n3, 0)
   const n1 = _createComponentWithFallback(_component_Comp)
+  const n2 = _child(n3)
   _renderEffect(() => {
     _setProp(n3, "id", _ctx.foo)
     _setText(n2, _toDisplayString(_ctx.bar))
@@ -220,9 +220,9 @@ export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n3 = t0()
   const n1 = _child(n3)
-  _setInsertionState(n1)
+  _setInsertionState(n1, null)
   const n0 = _createSlot("default", null)
-  _setInsertionState(n3, null)
+  _setInsertionState(n3, 1)
   const n2 = _createComponentWithFallback(_component_Comp)
   return n3
 }"
@@ -280,30 +280,6 @@ export function render(_ctx) {
 }"
 `;
 
-exports[`compile > setInsertionState > next, child and nthChild should be above the setInsertionState 1`] = `
-"import { resolveComponent as _resolveComponent, child as _child, next as _next, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, nthChild as _nthChild, createIf as _createIf, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue';
-const t0 = _template("<div></div>")
-const t1 = _template("<div><div></div><!><div></div><!><div><button></button></div></div>", true)
-
-export function render(_ctx) {
-  const _component_Comp = _resolveComponent("Comp")
-  const n6 = t1()
-  const n5 = _next(_child(n6))
-  const n7 = _nthChild(n6, 3)
-  const p0 = _next(n7)
-  const n4 = _child(p0)
-  _setInsertionState(n6, n5)
-  const n0 = _createComponentWithFallback(_component_Comp)
-  _setInsertionState(n6, n7)
-  const n1 = _createIf(() => (true), () => {
-    const n3 = t0()
-    return n3
-  })
-  _renderEffect(() => _setProp(n4, "disabled", _ctx.foo))
-  return n6
-}"
-`;
-
 exports[`compile > static + dynamic root 1`] = `
 "import { toDisplayString as _toDisplayString, setText as _setText, template as _template } from 'vue';
 const t0 = _template(" ")

+ 0 - 17
packages/compiler-vapor/__tests__/compile.spec.ts

@@ -221,23 +221,6 @@ describe('compile', () => {
     })
   })
 
-  describe('setInsertionState', () => {
-    test('next, child and nthChild should be above the setInsertionState', () => {
-      const code = compile(`
-      <div>
-        <div />
-        <Comp />
-        <div />
-        <div v-if="true" />
-        <div>
-          <button :disabled="foo" />
-        </div>
-      </div>
-      `)
-      expect(code).toMatchSnapshot()
-    })
-  })
-
   describe('execution order', () => {
     test('basic', () => {
       const code = compile(`<div :id="foo">{{ bar }}</div>`)

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap

@@ -87,7 +87,7 @@ const t1 = _template("<div></div>", true)
 export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
     const n5 = t1()
-    _setInsertionState(n5)
+    _setInsertionState(n5, null)
     const n2 = _createFor(() => (_for_item0.value), (_for_item1) => {
       const n4 = t0()
       const x4 = _txt(n4)

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

@@ -42,7 +42,7 @@ const t0 = _template("<div></div>", true)
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = t0()
-  _setInsertionState(n1)
+  _setInsertionState(n1, null)
   const n0 = _createComponentWithFallback(_component_Comp, { id: () => (_ctx.foo) }, null, null, true)
   return n1
 }"

+ 0 - 1
packages/compiler-vapor/src/generate.ts

@@ -86,7 +86,6 @@ export class CodegenContext {
       isTS: false,
       inSSR: false,
       inline: false,
-      vapor: false,
       bindingMetadata: {},
       expressionPlugins: [],
     }

+ 1 - 1
packages/compiler-vapor/src/generators/block.ts

@@ -71,7 +71,7 @@ export function genBlockContent(
   }
   for (const child of dynamic.children) {
     if (!child.hasDynamicChild) {
-      push(...genChildren(child, context, push, `n${child.id!}`))
+      push(...genChildren(child, context, `n${child.id!}`))
     }
   }
 

+ 7 - 3
packages/compiler-vapor/src/generators/operation.ts

@@ -168,7 +168,7 @@ function genInsertionState(
   operation: InsertionStateTypes,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { parent, anchor } = operation
+  const { parent, anchor, append } = operation
   return [
     NEWLINE,
     ...genCall(
@@ -178,8 +178,12 @@ function genInsertionState(
         ? undefined
         : anchor === -1 // -1 indicates prepend
           ? `0` // runtime anchor value for prepend
-          : anchor === -2 // -2 indicates append
-            ? `null` // runtime anchor value for append
+          : append // -2 indicates append
+            ? // null or number > 0 for append
+              // number > 0 is used for locate the previous static node during hydration
+              anchor === 0
+              ? 'null'
+              : `${anchor}`
             : `n${anchor}`,
     ),
   ]

+ 7 - 8
packages/compiler-vapor/src/generators/template.ts

@@ -36,7 +36,7 @@ export function genSelf(
   }
 
   if (hasDynamicChild) {
-    push(...genChildren(dynamic, context, push, `n${id}`))
+    push(...genChildren(dynamic, context, `n${id}`))
   }
 
   return frag
@@ -45,7 +45,6 @@ export function genSelf(
 export function genChildren(
   dynamic: IRDynamicInfo,
   context: CodegenContext,
-  pushBlock: (...items: CodeFragment[]) => number,
   from: string = `n${dynamic.id}`,
 ): CodeFragment[] {
   const { helper } = context
@@ -76,17 +75,17 @@ export function genChildren(
     // p for "placeholder" variables that are meant for possible reuse by
     // other access paths
     const variable = id === undefined ? `p${context.block.tempId++}` : `n${id}`
-    pushBlock(NEWLINE, `const ${variable} = `)
+    push(NEWLINE, `const ${variable} = `)
 
     if (prev) {
       if (elementIndex - prev[1] === 1) {
-        pushBlock(...genCall(helper('next'), prev[0]))
+        push(...genCall(helper('next'), prev[0]))
       } else {
-        pushBlock(...genCall(helper('nthChild'), from, String(elementIndex)))
+        push(...genCall(helper('nthChild'), from, String(elementIndex)))
       }
     } else {
       if (elementIndex === 0) {
-        pushBlock(...genCall(helper('child'), from))
+        push(...genCall(helper('child'), from))
       } else {
         // check if there's a node that we can reuse from
         let init = genCall(helper('child'), from)
@@ -95,7 +94,7 @@ export function genChildren(
         } else if (elementIndex > 1) {
           init = genCall(helper('nthChild'), from, String(elementIndex))
         }
-        pushBlock(...init)
+        push(...init)
       }
     }
 
@@ -108,7 +107,7 @@ export function genChildren(
     }
 
     prev = [variable, elementIndex]
-    push(...genChildren(child, context, pushBlock, variable))
+    push(...genChildren(child, context, variable))
   }
 
   return frag

+ 4 - 0
packages/compiler-vapor/src/ir/index.ts

@@ -80,6 +80,7 @@ export interface IfIRNode extends BaseIRNode {
   once?: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 
@@ -100,6 +101,7 @@ export interface ForIRNode extends BaseIRNode, IRFor {
   onlyChild: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 
@@ -202,6 +204,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
   dynamic?: SimpleExpressionNode
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
   scopeId?: string | null
 }
@@ -220,6 +223,7 @@ export interface SlotOutletIRNode extends BaseIRNode {
   forwarded?: boolean
   parent?: number
   anchor?: number
+  append?: boolean
   childIndex?: number
 }
 

+ 8 - 14
packages/compiler-vapor/src/transforms/transformChildren.ts

@@ -59,19 +59,17 @@ export const transformChildren: NodeTransform = (node, context) => {
 
 function processDynamicChildren(context: TransformContext<ElementNode>) {
   let prevDynamics: IRDynamicInfo[] = []
-  let hasStaticTemplate = false
-  let dynamicCount = 0
+  let staticCount = 0
   const children = context.dynamic.children
 
   for (const [index, child] of children.entries()) {
     if (child.flags & DynamicFlag.INSERT) {
       prevDynamics.push(child)
-      dynamicCount++
     }
 
     if (!(child.flags & DynamicFlag.NON_TEMPLATE)) {
       if (prevDynamics.length) {
-        if (hasStaticTemplate) {
+        if (staticCount) {
           context.childrenTemplate[index - prevDynamics.length] = `<!>`
           prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
           const anchor = (prevDynamics[0].anchor = context.increaseId())
@@ -81,25 +79,20 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
         }
         prevDynamics = []
       }
-      hasStaticTemplate = true
+      staticCount++
     }
   }
 
   if (prevDynamics.length) {
-    registerInsertion(
-      prevDynamics,
-      context,
-      // When there is only one dynamic node, no anchor is needed,
-      // firstChild is used as the hydration node
-      dynamicCount === 1 && !hasStaticTemplate ? undefined : -2 /* append */,
-    )
+    registerInsertion(prevDynamics, context, staticCount, true)
   }
 }
 
 function registerInsertion(
   dynamics: IRDynamicInfo[],
   context: TransformContext,
-  anchor?: number,
+  anchor: number,
+  append?: boolean,
 ) {
   for (const child of dynamics) {
     if (child.template != null) {
@@ -108,12 +101,13 @@ function registerInsertion(
         type: IRNodeTypes.INSERT_NODE,
         elements: dynamics.map(child => child.id!),
         parent: context.reference(),
-        anchor: anchor === -2 ? undefined : anchor,
+        anchor: append ? undefined : anchor,
       })
     } else if (child.operation && isBlockOperation(child.operation)) {
       // block types
       child.operation.parent = context.reference()
       child.operation.anchor = anchor
+      child.operation.append = append
     }
   }
 }

Разница между файлами не показана из-за своего большого размера
+ 143 - 376
packages/runtime-vapor/__tests__/hydration.spec.ts


+ 1 - 2
packages/runtime-vapor/src/apiCreateDynamicComponent.ts

@@ -9,7 +9,6 @@ import {
   insertionParent,
   resetInsertionState,
 } from './insertionState'
-import { DYNAMIC_COMPONENT_ANCHOR_LABEL } from '@vue/shared'
 import { advanceHydrationNode, isHydrating } from './dom/hydration'
 import { DynamicFragment, type VaporFragment } from './fragment'
 
@@ -27,7 +26,7 @@ export function createDynamicComponent(
 
   const frag =
     isHydrating || __DEV__
-      ? new DynamicFragment(DYNAMIC_COMPONENT_ANCHOR_LABEL)
+      ? new DynamicFragment('dynamic-component')
       : new DynamicFragment()
 
   renderEffect(() => {

+ 4 - 7
packages/runtime-vapor/src/apiCreateFor.ts

@@ -11,7 +11,7 @@ import {
   toReadonly,
   watch,
 } from '@vue/reactivity'
-import { FOR_ANCHOR_LABEL, isArray, isObject, isString } from '@vue/shared'
+import { isArray, isObject, isString } from '@vue/shared'
 import { createComment, createTextNode } from './dom/node'
 import {
   type Block,
@@ -27,9 +27,8 @@ import { renderEffect } from './renderEffect'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
 import {
   advanceHydrationNode,
-  currentHydrationNode,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
 } from './dom/hydration'
 import { ForFragment, VaporFragment } from './fragment'
@@ -135,10 +134,8 @@ export const createFor = (
       }
 
       if (isHydrating) {
-        parentAnchor = locateFragmentAnchor(
-          currentHydrationNode!,
-          FOR_ANCHOR_LABEL,
-        )!
+        parentAnchor = locateFragmentEndAnchor()!
+        // TODO: special handling vFor not render as a fragment. (inside Transition/TransitionGroup)
         if (__DEV__ && !parentAnchor) {
           throw new Error(`v-for fragment anchor node was not found.`)
         }

+ 1 - 4
packages/runtime-vapor/src/apiCreateIf.ts

@@ -1,4 +1,3 @@
-import { IF_ANCHOR_LABEL } from '@vue/shared'
 import { type Block, type BlockFn, insert } from './block'
 import { advanceHydrationNode, isHydrating } from './dom/hydration'
 import {
@@ -24,9 +23,7 @@ export function createIf(
     frag = condition() ? b1() : b2 ? b2() : []
   } else {
     frag =
-      isHydrating || __DEV__
-        ? new DynamicFragment(IF_ANCHOR_LABEL)
-        : new DynamicFragment()
+      isHydrating || __DEV__ ? new DynamicFragment('if') : new DynamicFragment()
     renderEffect(() => (frag as DynamicFragment).update(condition() ? b1 : b2))
   }
 

+ 2 - 1
packages/runtime-vapor/src/block.ts

@@ -19,6 +19,7 @@ import {
   type VaporFragment,
   isFragment,
 } from './fragment'
+import { child } from './dom/node'
 
 export interface TransitionOptions {
   $key?: any
@@ -71,7 +72,7 @@ export function insert(
   anchor: Node | null | 0 = null, // 0 means prepend
   parentSuspense?: any, // TODO Suspense
 ): void {
-  anchor = anchor === 0 ? parent.firstChild : anchor
+  anchor = anchor === 0 ? child(parent) : anchor
   if (block instanceof Node) {
     if (!isHydrating) {
       // only apply transition on Element nodes

+ 2 - 9
packages/runtime-vapor/src/componentSlots.ts

@@ -1,11 +1,4 @@
-import {
-  EMPTY_OBJ,
-  NO,
-  SLOT_ANCHOR_LABEL,
-  hasOwn,
-  isArray,
-  isFunction,
-} from '@vue/shared'
+import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
 import { type Block, type BlockFn, insert, setScopeId } from './block'
 import { rawPropsProxyHandlers } from './componentProps'
 import { currentInstance, isRef } from '@vue/runtime-dom'
@@ -142,7 +135,7 @@ export function createSlot(
   } else {
     fragment =
       isHydrating || __DEV__
-        ? new DynamicFragment(SLOT_ANCHOR_LABEL)
+        ? new DynamicFragment('slot')
         : new DynamicFragment()
     const isDynamicName = isFunction(name)
     const renderSlot = () => {

+ 45 - 44
packages/runtime-vapor/src/dom/hydration.ts

@@ -1,18 +1,17 @@
 import { warn } from '@vue/runtime-dom'
 import {
+  type ChildItem,
+  getHydrationState,
   insertionAnchor,
   insertionParent,
   resetInsertionState,
   setInsertionState,
 } from '../insertionState'
 import {
-  _child,
-  _next,
   createTextNode,
   disableHydrationNodeLookup,
   enableHydrationNodeLookup,
 } from './node'
-import { BLOCK_ANCHOR_END_LABEL, BLOCK_ANCHOR_START_LABEL } from '@vue/shared'
 
 const isHydratingStack = [] as boolean[]
 export let isHydrating = false
@@ -30,7 +29,7 @@ function performHydration<T>(
     locateHydrationNode = locateHydrationNodeImpl
     // optimize anchor cache lookup
     ;(Comment.prototype as any).$fe = undefined
-    ;(Node.prototype as any).$lbn = undefined
+    ;(Node.prototype as any).$idx = undefined
     isOptimized = true
   }
   enableHydrationNodeLookup()
@@ -135,14 +134,48 @@ function adoptTemplateImpl(node: Node, template: string): Node | null {
 function locateHydrationNodeImpl(): void {
   let node: Node | null
   if (insertionAnchor !== undefined) {
-    // prepend / insert / append
-    node = insertionParent!.$lbn = locateNextBlockNode(
-      insertionParent!.$lbn || _child(insertionParent!),
-    )!
+    const hydrationState = getHydrationState(insertionParent!)!
+    const {
+      prevDynamicCount,
+      logicalChildren,
+      appendAnchor,
+      insertionAnchors,
+    } = hydrationState
+    // prepend
+    if (insertionAnchor === 0) {
+      node = logicalChildren[prevDynamicCount]
+    }
+    // insert
+    else if (insertionAnchor instanceof Node) {
+      const seen =
+        (insertionAnchors && insertionAnchors.get(insertionAnchor)) || 0
+      node = seen
+        ? logicalChildren[(insertionAnchor as ChildItem).$idx + seen]
+        : insertionAnchor
+
+      hydrationState.insertionAnchors = (
+        hydrationState.insertionAnchors || new Map()
+      ).set(insertionAnchor, seen + 1)
+    }
+    // append
+    else {
+      if (appendAnchor) {
+        node = logicalChildren[(appendAnchor as ChildItem).$idx + 1]
+      } else {
+        node =
+          insertionAnchor === null
+            ? logicalChildren[0]
+            : // insertionAnchor is a number > 0
+              // indicates how many static nodes precede the node to append
+              logicalChildren[prevDynamicCount + insertionAnchor]
+      }
+      hydrationState.appendAnchor = node
+    }
+    hydrationState.prevDynamicCount++
   } else {
     node = currentHydrationNode
     if (insertionParent && (!node || node.parentNode !== insertionParent)) {
-      node = _child(insertionParent)
+      node = insertionParent.firstChild
     }
   }
 
@@ -181,43 +214,11 @@ export function locateEndAnchor(
   return null
 }
 
-export function locateFragmentAnchor(
-  node: Node,
-  label: string,
-): Comment | null {
-  while (node && node.nodeType === 8) {
+export function locateFragmentEndAnchor(label: string = ']'): Comment | null {
+  let node = currentHydrationNode!
+  while (node) {
     if (isComment(node, label)) return node
     node = node.nextSibling!
   }
   return null
 }
-
-function locateNextBlockNode(node: Node): Node | null {
-  while (node) {
-    if (isComment(node, BLOCK_ANCHOR_START_LABEL)) return node.nextSibling
-    node = node.nextSibling!
-  }
-
-  if (__DEV__) {
-    throw new Error(
-      `Could not locate hydration node with anchor label: ${BLOCK_ANCHOR_START_LABEL}`,
-    )
-  }
-  return null
-}
-
-export function advanceToNonBlockNode(node: Node): Node {
-  while (node) {
-    if (isComment(node, BLOCK_ANCHOR_START_LABEL)) {
-      node = locateEndAnchor(
-        node,
-        BLOCK_ANCHOR_START_LABEL,
-        BLOCK_ANCHOR_END_LABEL,
-      )!
-      continue
-    }
-
-    break
-  }
-  return node
-}

+ 57 - 35
packages/runtime-vapor/src/dom/node.ts

@@ -1,33 +1,37 @@
-import { advanceToNonBlockNode } from './hydration'
-import { isBlockStartAnchor } from '@vue/shared'
+/* @__NO_SIDE_EFFECTS__ */
+
+import {
+  type ChildItem,
+  getHydrationState,
+  getTemplateChildren,
+} from '../insertionState'
 
-/*! #__NO_SIDE_EFFECTS__ */
 export function createElement(tagName: string): HTMLElement {
   return document.createElement(tagName)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function createTextNode(value = ''): Text {
   return document.createTextNode(value)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function createComment(data: string): Comment {
   return document.createComment(data)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function querySelector(selectors: string): Element | null {
   return document.querySelector(selectors)
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 const _txt: typeof _child = _child
 
 /**
  * Hydration-specific version of `child`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 const __txt: typeof __child = (node: ParentNode): Node => {
   let n = node.firstChild!
 
@@ -41,54 +45,72 @@ const __txt: typeof __child = (node: ParentNode): Node => {
   return n
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _child(node: ParentNode): Node {
-  return node.firstChild!
+  const templateChildren = getTemplateChildren(node)
+  return templateChildren ? templateChildren[0] : node.firstChild!
 }
 
 /**
  * Hydration-specific version of `child`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
-export function __child(node: ParentNode): Node {
-  let n: Node = node.firstChild!
-  while (n && isBlockStartAnchor(n)) {
-    n = advanceToNonBlockNode(n)
-    n = n.nextSibling!
-  }
-
-  return n
+/* @__NO_SIDE_EFFECTS__ */
+export function __child(node: ParentNode & { $lpn?: Node }): Node {
+  return __nthChild(node, 0)!
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _nthChild(node: Node, i: number): Node {
-  return node.childNodes[i]
+  const templateChildren = getTemplateChildren(node as ParentNode)
+  return templateChildren ? templateChildren[i] : node.childNodes[i]
 }
 
 /**
  * Hydration-specific version of `nthChild`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function __nthChild(node: Node, i: number): Node {
-  let n = __child(node as ParentNode)
-  for (let start = 0; start < i; start++) {
-    n = __next(n) as ChildNode
+  const hydrationState = getHydrationState(node as ParentNode)
+  if (hydrationState) {
+    const { prevDynamicCount, insertionAnchors, logicalChildren } =
+      hydrationState
+    // prevDynamicCount tracks how many dynamic nodes have been processed
+    // so far (prepend/insert/append).
+    // For anchor-based insert, the first time an anchor is used we adopt the
+    // anchor node itself and do NOT consume the next child in `logicalChildren`,
+    // yet prevDynamicCount is still incremented. This overcounts the base
+    // offset by 1 per unique anchor that has appeared.
+    // insertionAnchors.size equals the number of unique anchors seen, so we
+    // subtract it to neutralize those "first-use doesn't consume" cases:
+    //   base = prevDynamicCount - insertionAnchors.size
+    // Then index from this base: logicalChildren[base + i].
+    const size = insertionAnchors ? insertionAnchors.size : 0
+    return logicalChildren[prevDynamicCount - size + i]
   }
-  return n
+  return node.childNodes[i]
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function _next(node: Node): Node {
-  return node.nextSibling!
+  const templateChildren = getTemplateChildren(node.parentNode!)
+  return templateChildren
+    ? templateChildren[(node as ChildItem).$idx + 1]
+    : node.nextSibling!
 }
 
 /**
  * Hydration-specific version of `next`.
  */
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export function __next(node: Node): Node {
-  if (isBlockStartAnchor(node)) {
-    node = advanceToNonBlockNode(node)
+  const hydrationState = getHydrationState(node.parentNode!)
+  if (hydrationState) {
+    const { logicalChildren, insertionAnchors } = hydrationState
+    const seenCount = (insertionAnchors && insertionAnchors.get(node)) || 0
+    // If node is used as an anchor, the first hydration uses node itself,
+    // but seenCount increases, so here needs -1
+    const insertedNodesCount = seenCount === 0 ? 0 : seenCount - 1
+    return logicalChildren[(node as ChildItem).$idx + insertedNodesCount + 1]
   }
   return node.nextSibling!
 }
@@ -97,25 +119,25 @@ type DelegatedFunction<T extends (...args: any[]) => any> = T & {
   impl: T
 }
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const txt: DelegatedFunction<typeof _txt> = node => {
   return txt.impl(node)
 }
 txt.impl = _child
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const child: DelegatedFunction<typeof _child> = node => {
   return child.impl(node)
 }
 child.impl = _child
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const next: DelegatedFunction<typeof _next> = node => {
   return next.impl(node)
 }
 next.impl = _next
 
-/*! #__NO_SIDE_EFFECTS__ */
+/* @__NO_SIDE_EFFECTS__ */
 export const nthChild: DelegatedFunction<typeof _nthChild> = (node, i) => {
   return nthChild.impl(node, i)
 }

+ 1 - 0
packages/runtime-vapor/src/dom/template.ts

@@ -18,6 +18,7 @@ export function template(html: string, root?: boolean) {
       if (root) (adopted as any).$root = true
       return adopted
     }
+
     // fast path for text nodes
     if (html[0] !== '<') {
       return createTextNode(html)

+ 47 - 12
packages/runtime-vapor/src/fragment.ts

@@ -12,16 +12,16 @@ import {
 import type { TransitionHooks } from '@vue/runtime-dom'
 import {
   advanceHydrationNode,
-  currentHydrationNode,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
 } from './dom/hydration'
 import {
   applyTransitionHooks,
   applyTransitionLeaveHooks,
 } from './components/Transition'
-import type { VaporComponentInstance } from './component'
+import { type VaporComponentInstance, isVaporComponent } from './component'
+import { isArray } from '@vue/shared'
 
 export class VaporFragment<T extends Block = Block>
   implements TransitionOptions
@@ -74,7 +74,7 @@ export class DynamicFragment extends VaporFragment {
 
   update(render?: BlockFn, key: any = render): void {
     if (key === this.current) {
-      if (isHydrating) this.hydrate(this.anchorLabel!)
+      if (isHydrating) this.hydrate(true)
       return
     }
     this.current = key
@@ -139,19 +139,41 @@ export class DynamicFragment extends VaporFragment {
 
     setActiveSub(prevSub)
 
-    if (isHydrating) this.hydrate(this.anchorLabel!)
+    if (isHydrating) this.hydrate()
   }
 
-  hydrate = (label: string): void => {
-    // avoid repeated hydration during rendering fallback
+  hydrate = (isEmpty = false): void => {
+    // avoid repeated hydration during fallback rendering
     if (this.anchor) return
 
-    this.anchor = locateFragmentAnchor(currentHydrationNode!, label)!
-    if (this.anchor) {
-      advanceHydrationNode(this.anchor)
-    } else if (__DEV__) {
-      throw new Error(`${label} fragment anchor node was not found.`)
+    // reuse the empty comment node as the anchor for empty if
+    if (this.anchorLabel === 'if' && isEmpty) {
+      this.anchor = locateFragmentEndAnchor('')!
+      if (!this.anchor) {
+        throw new Error('Failed to locate if anchor')
+      } else {
+        ;(this.anchor as Comment).data = this.anchorLabel
+        return
+      }
+    }
+
+    // reuse the vdom fragment end anchor for slots
+    if (this.anchorLabel === 'slot') {
+      this.anchor = locateFragmentEndAnchor()!
+      if (!this.anchor) {
+        throw new Error('Failed to locate slot anchor')
+      } else {
+        return
+      }
     }
+
+    // create an anchor
+    const { parentNode, nextSibling } = findLastChild(this)!
+    parentNode!.insertBefore(
+      (this.anchor = createComment(this.anchorLabel!)),
+      nextSibling,
+    )
+    advanceHydrationNode(this.anchor)
   }
 }
 
@@ -195,3 +217,16 @@ function findInvalidFragment(fragment: VaporFragment): VaporFragment | null {
     ? findInvalidFragment(fragment.nodes) || fragment
     : fragment
 }
+
+export function findLastChild(node: Block): Node | undefined | null {
+  if (node && node instanceof Node) {
+    return node
+  } else if (isArray(node)) {
+    return findLastChild(node[node.length - 1])
+  } else if (isVaporComponent(node)) {
+    return findLastChild(node.block!)
+  } else {
+    if (node instanceof DynamicFragment && node.anchor) return node.anchor
+    return findLastChild(node.nodes!)
+  }
+}

+ 107 - 8
packages/runtime-vapor/src/insertionState.ts

@@ -1,11 +1,20 @@
-export let insertionParent:
-  | (ParentNode & {
-      // the last hydrated block node
-      $lbn?: Node
-    })
-  | undefined
+import { isHydrating } from './dom/hydration'
+export interface ChildItem extends ChildNode {
+  $idx: number
+}
+type HydrationState = {
+  logicalChildren: ChildItem[]
+  prevDynamicCount: number
+  insertionAnchors: Map<Node, number> | null
+  appendAnchor: Node | null
+}
+export let insertionParent: ParentNode | undefined
 export let insertionAnchor: Node | 0 | undefined | null
 
+const templateChildrenCache = new WeakMap<ParentNode, ChildItem[]>()
+
+const hydrationStateCache = new WeakMap<ParentNode, HydrationState>()
+
 /**
  * This function is called before a block type that requires insertion
  * (component, slot outlet, if, for) is created. The state is used for actual
@@ -13,12 +22,102 @@ export let insertionAnchor: Node | 0 | undefined | null
  */
 export function setInsertionState(
   parent: ParentNode,
-  anchor?: Node | 0 | null,
+  anchor?: Node | 0 | null | number,
 ): void {
   insertionParent = parent
-  insertionAnchor = anchor
+  if (isHydrating) {
+    initializeHydrationState(anchor, parent)
+  } else {
+    cacheTemplateChildren(anchor, parent)
+  }
+}
+
+function initializeHydrationState(
+  anchor: number | Node | null | undefined,
+  parent: ParentNode,
+) {
+  insertionAnchor = anchor as Node
+  if (!hydrationStateCache.has(parent)) {
+    const childNodes = parent.childNodes
+    const len = childNodes.length
+    const logicalChildren = new Array(len) as ChildItem[]
+    // Build logical children:
+    // - static node: keep the node as a child
+    // - fragment: keep only the start anchor ('<!--[-->') as a child
+    let index = 0
+    for (let i = 0; i < len; i++) {
+      const n = childNodes[i] as ChildItem
+      if (n.nodeType === 8) {
+        const data = (n as any as Comment).data
+        // vdom fragment
+        if (data === '[') {
+          n.$idx = index
+          logicalChildren[index++] = n
+          // find matching end anchor, accounting for nested fragments
+          let depth = 1
+          let j = i + 1
+          for (; j < len; j++) {
+            const c = childNodes[j] as Comment
+            if (c.nodeType === 8) {
+              const d = c.data
+              if (d === '[') depth++
+              else if (d === ']') {
+                depth--
+                if (depth === 0) break
+              }
+            }
+          }
+          // jump i to the end anchor
+          i = j
+          continue
+        }
+      }
+      n.$idx = index
+      logicalChildren[index++] = n
+    }
+    logicalChildren.length = index
+    hydrationStateCache.set(parent, {
+      logicalChildren,
+      prevDynamicCount: 0,
+      insertionAnchors: null,
+      appendAnchor: null,
+    })
+  }
+}
+
+function cacheTemplateChildren(
+  anchor: number | Node | null | undefined,
+  parent: ParentNode,
+) {
+  // special handling append anchor value to null
+  insertionAnchor =
+    typeof anchor === 'number' && anchor > 0 ? null : (anchor as Node)
+
+  if (!templateChildrenCache.has(parent)) {
+    const nodes = parent.childNodes
+    const len = nodes.length
+    const children = new Array(len)
+    for (let i = 0; i < len; i++) {
+      const node = nodes[i] as ChildItem
+      node.$idx = i
+      children[i] = node
+    }
+    templateChildrenCache.set(parent, children)
+  }
 }
 
 export function resetInsertionState(): void {
   insertionParent = insertionAnchor = undefined
 }
+
+export function getTemplateChildren(
+  parent: ParentNode,
+): ChildItem[] | undefined {
+  return templateChildrenCache.get(parent)
+}
+
+export function getHydrationState(
+  parent: ParentNode,
+): HydrationState | undefined {
+  return hydrationStateCache.get(parent)
+}

+ 5 - 9
packages/runtime-vapor/src/vdomInterop.ts

@@ -59,7 +59,7 @@ import {
   currentHydrationNode,
   isComment,
   isHydrating,
-  locateFragmentAnchor,
+  locateFragmentEndAnchor,
   locateHydrationNode,
   setCurrentHydrationNode,
   hydrateNode as vaporHydrateNode,
@@ -198,18 +198,14 @@ const vaporInteropImpl: Omit<
     const propsRef = (vnode.vs!.ref = shallowRef(vnode.props))
     vaporHydrateNode(node, () => {
       vnode.vb = slot(new Proxy(propsRef, vaporSlotPropsProxyHandler))
-      vnode.el = vnode.anchor = locateFragmentAnchor(
-        currentHydrationNode!,
-        // locate the vdom fragment end anchor (<!--]-->), since no vapor slot
-        // anchor (<!--slot-->) is injected in vdom component
-        ']',
-      )
+      vnode.el = currentHydrationNode!
+      vnode.anchor = locateFragmentEndAnchor()
 
       if (__DEV__ && !vnode.anchor) {
-        throw new Error(`vapor slot anchor node was not found.`)
+        throw new Error(`Failed to locate slot anchor`)
       }
     })
-    return _next(node)
+    return _next(vnode.anchor as Node)
   },
 }
 

+ 2 - 15
packages/server-renderer/src/helpers/ssrRenderSlot.ts

@@ -5,7 +5,7 @@ import {
   type SSRBufferItem,
   renderVNodeChildren,
 } from '../render'
-import { isArray, isString } from '@vue/shared'
+import { isArray } from '@vue/shared'
 
 const { ensureValidVNode } = ssrUtils
 
@@ -83,20 +83,7 @@ export function ssrRenderSlotInner(
         isEmptySlot = false
       } else {
         for (let i = 0; i < slotBuffer.length; i++) {
-          const buffer = slotBuffer[i]
-
-          // preserve empty slot anchor in vapor components
-          // DynamicFragment requires this anchor
-          if (
-            parentComponent.type.__vapor &&
-            isString(buffer) &&
-            buffer === '<!--slot-->'
-          ) {
-            push(buffer)
-            continue
-          }
-
-          if (!isComment(buffer)) {
+          if (!isComment(slotBuffer[i])) {
             isEmptySlot = false
             break
           }

+ 5 - 11
packages/server-renderer/src/render.ts

@@ -236,17 +236,11 @@ export function renderVNode(
       push(escapeHtml(children as string))
       break
     case Comment:
-      if (children) {
-        const content = children as string
-        // avoid escaping comments
-        if (content.startsWith('<!--') && content.endsWith('-->')) {
-          push(content)
-        } else {
-          push(`<!--${escapeHtmlComment(content)}-->`)
-        }
-      } else {
-        push(`<!---->`)
-      }
+      push(
+        children
+          ? `<!--${escapeHtmlComment(children as string)}-->`
+          : `<!---->`,
+      )
       break
     case Static:
       push(children as string)

+ 0 - 12
packages/shared/src/domAnchors.ts

@@ -1,12 +0,0 @@
-export const BLOCK_ANCHOR_START_LABEL = '[['
-export const BLOCK_ANCHOR_END_LABEL = ']]'
-export const IF_ANCHOR_LABEL: string = 'if'
-export const DYNAMIC_COMPONENT_ANCHOR_LABEL: string = 'dynamic-component'
-export const FOR_ANCHOR_LABEL: string = 'for'
-export const SLOT_ANCHOR_LABEL: string = 'slot'
-
-export function isBlockStartAnchor(node: Node): node is Comment {
-  if (node.nodeType !== 8) return false
-  const data = (node as Comment).data
-  return data === `${BLOCK_ANCHOR_START_LABEL}`
-}

+ 0 - 1
packages/shared/src/index.ts

@@ -13,5 +13,4 @@ export * from './looseEqual'
 export * from './toDisplayString'
 export * from './typeUtils'
 export * from './subSequence'
-export * from './domAnchors'
 export * from './cssVars'

Некоторые файлы не были показаны из-за большого количества измененных файлов