]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: tweak v-skip on component
authordaiwei <daiwei521@126.com>
Fri, 24 Jan 2025 09:26:43 +0000 (17:26 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 24 Jan 2025 09:26:43 +0000 (17:26 +0800)
packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap
packages/compiler-core/src/runtimeHelpers.ts
packages/compiler-core/src/transforms/vSkip.ts
packages/compiler-ssr/__tests__/ssrVSkip.spec.ts
packages/compiler-ssr/src/runtimeHelpers.ts
packages/compiler-ssr/src/transforms/ssrVSkip.ts
packages/runtime-core/src/helpers/resolveAssets.ts
packages/runtime-core/src/index.ts
packages/server-renderer/src/helpers/ssrRenderSkipComponent.ts [new file with mode: 0644]
packages/server-renderer/src/internal.ts

index bb7cdf978f492dd2f0435f1175e40b2a4fdaee8b..8904400a65ae880f5253c059b2e57d5b53737fff 100644 (file)
@@ -36,106 +36,6 @@ return function render(_ctx, _cache) {
 }"
 `;
 
-exports[`compiler: v-skip > transform > on component 1`] = `
-"const _Vue = Vue
-
-return function render(_ctx, _cache) {
-  with (_ctx) {
-    const { withCtx: _withCtx, createCommentVNode: _createCommentVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue
-
-    const _component_Comp = _resolveComponent("Comp")
-
-    return (_ctx.ok)
-      ? _createCommentVNode("v-skip", true)
-      : (_openBlock(), _createBlock(_component_Comp, { key: 1 }))
-  }
-}"
-`;
-
-exports[`compiler: v-skip > transform > on component with default slot 1`] = `
-"const _Vue = Vue
-
-return function render(_ctx, _cache) {
-  with (_ctx) {
-    const { withCtx: _withCtx, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
-
-    const _component_Comp = _resolveComponent("Comp")
-
-    return (_ctx.ok)
-      ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, ["foo"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
-      : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
-          default: _withCtx(() => ["foo"]),
-          _: 1 /* STABLE */
-        }))
-  }
-}"
-`;
-
-exports[`compiler: v-skip > transform > on component with multiple implicit slot 1`] = `
-"const _Vue = Vue
-
-return function render(_ctx, _cache) {
-  with (_ctx) {
-    const { withCtx: _withCtx, createElementVNode: _createElementVNode, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
-
-    const _component_Comp = _resolveComponent("Comp")
-
-    return (_ctx.ok)
-      ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
-          _createElementVNode("span"),
-          _createElementVNode("div")
-        ], 64 /* STABLE_FRAGMENT */))
-      : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
-          foo: _withCtx(() => ["foo"]),
-          default: _withCtx(() => [
-            _createElementVNode("span"),
-            _createElementVNode("div")
-          ]),
-          _: 1 /* STABLE */
-        }))
-  }
-}"
-`;
-
-exports[`compiler: v-skip > transform > on component with multiple named slot 1`] = `
-"const _Vue = Vue
-
-return function render(_ctx, _cache) {
-  with (_ctx) {
-    const { withCtx: _withCtx, resolveComponent: _resolveComponent, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createBlock: _createBlock } = _Vue
-
-    const _component_Comp = _resolveComponent("Comp")
-
-    return (_ctx.ok)
-      ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, ["default"], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
-      : (_openBlock(), _createBlock(_component_Comp, { key: 1 }, {
-          default: _withCtx(() => ["default"]),
-          foo: _withCtx(() => ["foo"]),
-          _: 1 /* STABLE */
-        }))
-  }
-}"
-`;
-
-exports[`compiler: v-skip > transform > on dynamic component 1`] = `
-"const _Vue = Vue
-
-return function render(_ctx, _cache) {
-  with (_ctx) {
-    const { withCtx: _withCtx, renderSlot: _renderSlot, resolveDynamicComponent: _resolveDynamicComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue
-
-    return (_ctx.ok)
-      ? _renderSlot(_ctx.$slots, "default", { key: 0 })
-      : (_openBlock(), _createBlock(_resolveDynamicComponent(_ctx.Comp), { key: 1 }, {
-          default: _withCtx(() => [
-            _renderSlot(_ctx.$slots, "default", { key: 0 })
-          ]),
-          _: 3 /* FORWARDED */
-        }))
-  }
-}"
-`;
-
 exports[`compiler: v-skip > transform > v-else + v-skip 1`] = `
 "const _Vue = Vue
 
index 7cf3757b249ba8c2f8c97310f00bb4b9117a9547..46b36f45054d585e066a4ed4b5b1e5b39f76d7d3 100644 (file)
@@ -26,6 +26,9 @@ export const CREATE_STATIC: unique symbol = Symbol(
 export const RESOLVE_COMPONENT: unique symbol = Symbol(
   __DEV__ ? `resolveComponent` : ``,
 )
+export const RESOLVE_SKIP_COMPONENT: unique symbol = Symbol(
+  __DEV__ ? `resolveSkipComponent` : ``,
+)
 export const RESOLVE_DYNAMIC_COMPONENT: unique symbol = Symbol(
   __DEV__ ? `resolveDynamicComponent` : ``,
 )
@@ -99,6 +102,7 @@ export const helperNameMap: Record<symbol, string> = {
   [CREATE_STATIC]: `createStaticVNode`,
   [RESOLVE_COMPONENT]: `resolveComponent`,
   [RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
+  [RESOLVE_SKIP_COMPONENT]: `resolveSkipComponent`,
   [RESOLVE_DIRECTIVE]: `resolveDirective`,
   [RESOLVE_FILTER]: `resolveFilter`,
   [WITH_DIRECTIVES]: `withDirectives`,
index e9582e8e5a72e4b064e97831e4c2d67855559a68..cd04c8db89c9e73319ce7437edd0f0fb630e2f06 100644 (file)
@@ -2,11 +2,12 @@ import {
   type DirectiveNode,
   type ElementNode,
   ElementTypes,
+  type ExpressionNode,
   type IfBranchNode,
   NodeTypes,
   type SimpleExpressionNode,
   type SkipNode,
-  type TemplateChildNode,
+  type VNodeCall,
   createCallExpression,
   createConditionalExpression,
   createSimpleExpression,
@@ -19,7 +20,8 @@ import {
 import {
   CREATE_COMMENT,
   ErrorCodes,
-  buildSlots,
+  RESOLVE_SKIP_COMPONENT,
+  WITH_MEMO,
   createCompilerError,
   findDir,
   findProp,
@@ -33,19 +35,40 @@ import { cloneLoc } from '../parser'
 export const transformSkip: NodeTransform = createStructuralDirectiveTransform(
   'skip',
   (node, dir, context) => {
-    return processSkip(node, dir, context, skipNode => {
+    return processSkip(node, dir, context, (skipNode?: SkipNode) => {
       return () => {
-        const { consequent, alternate, test } = skipNode
-        const consequentNode =
-          consequent.type === NodeTypes.IF_BRANCH
-            ? createCodegenNodeForBranch(consequent, 0, context)
-            : consequent
+        const codegenNode = node.codegenNode!
+        if (node.tagType === ElementTypes.COMPONENT) {
+          if (codegenNode.type === NodeTypes.VNODE_CALL) {
+            codegenNode.tag = getVNodeTag(
+              context,
+              dir.exp!,
+              codegenNode.tag as string,
+            )
+          } else if (
+            codegenNode.type === NodeTypes.JS_CALL_EXPRESSION &&
+            codegenNode.callee === WITH_MEMO
+          ) {
+            const vnodeCall = codegenNode.arguments[1].returns as VNodeCall
+            vnodeCall.tag = getVNodeTag(
+              context,
+              dir.exp!,
+              vnodeCall.tag as string,
+            )
+          }
+        } else {
+          const { consequent, alternate, test } = skipNode!
+          const consequentNode =
+            consequent.type === NodeTypes.IF_BRANCH
+              ? createCodegenNodeForBranch(consequent, 0, context)
+              : consequent
 
-        skipNode.codegenNode = createConditionalExpression(
-          test,
-          consequentNode,
-          createCodegenNodeForBranch(alternate, 1, context),
-        )
+          skipNode!.codegenNode = createConditionalExpression(
+            test,
+            consequentNode,
+            createCodegenNodeForBranch(alternate, 1, context),
+          )
+        }
       }
     })
   },
@@ -55,7 +78,7 @@ export function processSkip(
   node: ElementNode,
   dir: DirectiveNode,
   context: TransformContext,
-  processCodegen?: (skipNode: SkipNode) => () => void,
+  processCodegen?: (skipNode?: SkipNode) => () => void,
 ): (() => void) | undefined {
   const loc = dir.exp ? dir.exp.loc : node.loc
   if (
@@ -83,64 +106,53 @@ export function processSkip(
     validateBrowserExpression(dir.exp as SimpleExpressionNode, context)
   }
 
-  let children: TemplateChildNode[] = []
-  // for components, extract default slot without props
-  // if not found, throw an error
-  if (node.tagType === ElementTypes.COMPONENT) {
-    const { slots } = buildSlots(node, context, undefined, true)
-    if (slots.type === NodeTypes.JS_OBJECT_EXPRESSION) {
-      const prop = slots.properties.find(
-        p =>
-          p.type === NodeTypes.JS_PROPERTY &&
-          p.key.type === NodeTypes.SIMPLE_EXPRESSION &&
-          p.key.content === 'default' &&
-          p.value.params === undefined,
-      )
-      if (prop) {
-        children = prop.value.returns as TemplateChildNode[]
-      } else {
-        context.onError(
-          createCompilerError(ErrorCodes.X_V_SKIP_UNEXPECTED_SLOT, loc),
-        )
-      }
-    }
-  }
-  // for plain elements, take all children
-  else {
-    children = node.children
-  }
+  let skipNode: SkipNode | undefined
+  if (node.tagType === ElementTypes.ELEMENT) {
+    const children = node.children
+    // if children is empty, create comment node
+    const consequent =
+      children.length !== 0
+        ? ({
+            type: NodeTypes.IF_BRANCH,
+            loc: node.loc,
+            condition: undefined,
+            children,
+            userKey: findProp(node, `key`),
+          } as IfBranchNode)
+        : createCallExpression(context.helper(CREATE_COMMENT), [
+            __DEV__ ? '"v-skip"' : '""',
+            'true',
+          ])
 
-  // if children is empty, create comment node
-  const consequent =
-    children.length !== 0
-      ? ({
-          type: NodeTypes.IF_BRANCH,
-          loc: node.loc,
-          condition: undefined,
-          children,
-          userKey: findProp(node, `key`),
-        } as IfBranchNode)
-      : createCallExpression(context.helper(CREATE_COMMENT), [
-          __DEV__ ? '"v-skip"' : '""',
-          'true',
-        ])
+    const alternate: IfBranchNode = {
+      type: NodeTypes.IF_BRANCH,
+      loc: node.loc,
+      condition: undefined,
+      children: [node],
+      userKey: findProp(node, `key`),
+    }
 
-  const alternate: IfBranchNode = {
-    type: NodeTypes.IF_BRANCH,
-    loc: node.loc,
-    condition: undefined,
-    children: [node],
-    userKey: findProp(node, `key`),
+    skipNode = {
+      type: NodeTypes.SKIP,
+      loc: cloneLoc(node.loc),
+      test: dir.exp,
+      consequent,
+      alternate,
+      newline: true,
+    }
+    context.replaceNode(skipNode)
   }
 
-  const skipNode: SkipNode = {
-    type: NodeTypes.SKIP,
-    loc: cloneLoc(node.loc),
-    test: dir.exp,
-    consequent,
-    alternate,
-    newline: true,
-  }
-  context.replaceNode(skipNode)
   if (processCodegen) return processCodegen(skipNode)
 }
+
+function getVNodeTag(
+  context: TransformContext,
+  exp: ExpressionNode,
+  tag: string,
+) {
+  return createCallExpression(context.helper(RESOLVE_SKIP_COMPONENT), [
+    exp,
+    tag,
+  ])
+}
index 3c1c8cfc026e5188c127fd2ffb11bfc791529b3f..4929912b6f3ef6c7b1d3b4931601c4b3f9a375de 100644 (file)
@@ -195,45 +195,37 @@ describe('ssr: v-skip', () => {
 
   test('on component', () => {
     expect(compile(`<Comp v-skip="foo"/>`).code).toMatchInlineSnapshot(`
-      "const { withCtx: _withCtx, createCommentVNode: _createCommentVNode, resolveComponent: _resolveComponent } = require("vue")
-      const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer")
+      "const { resolveComponent: _resolveComponent } = require("vue")
+      const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         const _component_Comp = _resolveComponent("Comp")
 
-        if (_ctx.foo) {
-          _createCommentVNode("v-skip", true)
-        } else {
-          _push(_ssrRenderComponent(_component_Comp, _attrs, null, _parent))
-        }
+        _push(_ssrRenderSkipComponent(_push, _ctx.foo, _component_Comp, _attrs, null, _parent))
       }"
     `)
   })
 
   test('on component with default slot', () => {
     expect(compile(`<Comp v-skip="ok">foo</Comp>`).code).toMatchInlineSnapshot(`
-      "const { withCtx: _withCtx, resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require("vue")
-      const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require("vue")
+      const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         const _component_Comp = _resolveComponent("Comp")
 
-        if (_ctx.ok) {
-          _push(\`<!--[-->foo<!--]-->\`)
-        } else {
-          _push(_ssrRenderComponent(_component_Comp, _attrs, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`foo\`)
-              } else {
-                return [
-                  _createTextVNode("foo")
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }, _parent))
-        }
+        _push(_ssrRenderSkipComponent(_push, _ctx.ok, _component_Comp, _attrs, {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`foo\`)
+            } else {
+              return [
+                _createTextVNode("foo")
+              ]
+            }
+          }),
+          _: 1 /* STABLE */
+        }, _parent))
       }"
     `)
   })
@@ -247,37 +239,33 @@ describe('ssr: v-skip', () => {
         </Comp>`,
       ).code,
     ).toMatchInlineSnapshot(`
-      "const { withCtx: _withCtx, resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require("vue")
-      const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require("vue")
+      const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         const _component_Comp = _resolveComponent("Comp")
 
-        if (_ctx.ok) {
-          _push(\`<!--[-->default<!--]-->\`)
-        } else {
-          _push(_ssrRenderComponent(_component_Comp, _attrs, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`default\`)
-              } else {
-                return [
-                  _createTextVNode("default")
-                ]
-              }
-            }),
-            foo: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`foo\`)
-              } else {
-                return [
-                  _createTextVNode("foo")
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }, _parent))
-        }
+        _push(_ssrRenderSkipComponent(_push, _ctx.ok, _component_Comp, _attrs, {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`default\`)
+            } else {
+              return [
+                _createTextVNode("default")
+              ]
+            }
+          }),
+          foo: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`foo\`)
+            } else {
+              return [
+                _createTextVNode("foo")
+              ]
+            }
+          }),
+          _: 1 /* STABLE */
+        }, _parent))
       }"
     `)
   })
@@ -292,42 +280,38 @@ describe('ssr: v-skip', () => {
         </Comp>`,
       ).code,
     ).toMatchInlineSnapshot(`
-      "const { withCtx: _withCtx, resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, createVNode: _createVNode } = require("vue")
-      const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode, createVNode: _createVNode } = require("vue")
+      const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
         const _component_Comp = _resolveComponent("Comp")
 
-        if (_ctx.ok) {
-          _push(\`<!--[--><span></span><div></div><!--]-->\`)
-        } else {
-          _push(_ssrRenderComponent(_component_Comp, _attrs, {
-            foo: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`foo\`)
-              } else {
-                return [
-                  _createTextVNode("foo")
-                ]
-              }
-            }),
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _push(\`<span\${
-                  _scopeId
-                }></span><div\${
-                  _scopeId
-                }></div>\`)
-              } else {
-                return [
-                  _createVNode("span"),
-                  _createVNode("div")
-                ]
-              }
-            }),
-            _: 1 /* STABLE */
-          }, _parent))
-        }
+        _push(_ssrRenderSkipComponent(_push, _ctx.ok, _component_Comp, _attrs, {
+          foo: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`foo\`)
+            } else {
+              return [
+                _createTextVNode("foo")
+              ]
+            }
+          }),
+          default: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`<span\${
+                _scopeId
+              }></span><div\${
+                _scopeId
+              }></div>\`)
+            } else {
+              return [
+                _createVNode("span"),
+                _createVNode("div")
+              ]
+            }
+          }),
+          _: 1 /* STABLE */
+        }, _parent))
       }"
     `)
   })
@@ -340,26 +324,22 @@ describe('ssr: v-skip', () => {
       </component>`,
       ).code,
     ).toMatchInlineSnapshot(`
-      "const { withCtx: _withCtx, resolveDynamicComponent: _resolveDynamicComponent, renderSlot: _renderSlot, createVNode: _createVNode } = require("vue")
-      const { ssrRenderSlot: _ssrRenderSlot, ssrRenderVNode: _ssrRenderVNode } = require("vue/server-renderer")
+      "const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx, renderSlot: _renderSlot, createVNode: _createVNode } = require("vue")
+      const { ssrRenderSlot: _ssrRenderSlot, ssrRenderVNode: _ssrRenderVNode, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
       return function ssrRender(_ctx, _push, _parent, _attrs) {
-        if (_ctx.ok) {
-          _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-        } else {
-          _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.Comp), _attrs, {
-            default: _withCtx((_, _push, _parent, _scopeId) => {
-              if (_push) {
-                _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
-              } else {
-                return [
-                  _renderSlot(_ctx.$slots, "default")
-                ]
-              }
-            }),
-            _: 3 /* FORWARDED */
-          }), _parent)
-        }
+        _ssrRenderSkipComponent(_push, _ctx.ok, _push, _createVNode(_resolveDynamicComponent(_ctx.Comp), _attrs, {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId)
+            } else {
+              return [
+                _renderSlot(_ctx.$slots, "default")
+              ]
+            }
+          }),
+          _: 3 /* FORWARDED */
+        }), _parent)
       }"
     `)
   })
@@ -371,31 +351,27 @@ describe('ssr: v-skip', () => {
       <Comp v-skip="ok"><span/></Comp>
     `).code,
     ).toMatchInlineSnapshot(`
-        "const { withCtx: _withCtx, resolveComponent: _resolveComponent, createVNode: _createVNode } = require("vue")
-        const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require("vue")
+      const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSkipComponent: _ssrRenderSkipComponent } = require("vue/server-renderer")
 
-        return function ssrRender(_ctx, _push, _parent, _attrs) {
-          const _component_Comp = _resolveComponent("Comp")
+      return function ssrRender(_ctx, _push, _parent, _attrs) {
+        const _component_Comp = _resolveComponent("Comp")
 
-          _push(\`<!--[--><div></div>\`)
-          if (_ctx.ok) {
-            _push(\`<span></span>\`)
-          } else {
-            _push(_ssrRenderComponent(_component_Comp, null, {
-              default: _withCtx((_, _push, _parent, _scopeId) => {
-                if (_push) {
-                  _push(\`<span\${_scopeId}></span>\`)
-                } else {
-                  return [
-                    _createVNode("span")
-                  ]
-                }
-              }),
-              _: 1 /* STABLE */
-            }, _parent))
-          }
-          _push(\`<!--]-->\`)
-        }"
-      `)
+        _push(\`<!--[--><div></div>\`)
+        _push(_ssrRenderSkipComponent(_push, _ctx.ok, _component_Comp, null, {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
+            if (_push) {
+              _push(\`<span\${_scopeId}></span>\`)
+            } else {
+              return [
+                _createVNode("span")
+              ]
+            }
+          }),
+          _: 1 /* STABLE */
+        }, _parent))
+        _push(\`<!--]-->\`)
+      }"
+    `)
   })
 })
index 0e2c8c67bed80bfa27e5490842b5835f1755a8b4..bc1e9e44e98305779bbb21b4bbeb173d7185e7f0 100644 (file)
@@ -3,6 +3,9 @@ import { registerRuntimeHelpers } from '@vue/compiler-dom'
 export const SSR_INTERPOLATE: unique symbol = Symbol(`ssrInterpolate`)
 export const SSR_RENDER_VNODE: unique symbol = Symbol(`ssrRenderVNode`)
 export const SSR_RENDER_COMPONENT: unique symbol = Symbol(`ssrRenderComponent`)
+export const SSR_RENDER_SKIP_COMPONENT: unique symbol = Symbol(
+  `ssrRenderSkipComponent`,
+)
 export const SSR_RENDER_SLOT: unique symbol = Symbol(`ssrRenderSlot`)
 export const SSR_RENDER_SLOT_INNER: unique symbol = Symbol(`ssrRenderSlotInner`)
 export const SSR_RENDER_CLASS: unique symbol = Symbol(`ssrRenderClass`)
@@ -32,6 +35,7 @@ export const ssrHelpers: Record<symbol, string> = {
   [SSR_INTERPOLATE]: `ssrInterpolate`,
   [SSR_RENDER_VNODE]: `ssrRenderVNode`,
   [SSR_RENDER_COMPONENT]: `ssrRenderComponent`,
+  [SSR_RENDER_SKIP_COMPONENT]: `ssrRenderSkipComponent`,
   [SSR_RENDER_SLOT]: `ssrRenderSlot`,
   [SSR_RENDER_SLOT_INNER]: `ssrRenderSlotInner`,
   [SSR_RENDER_CLASS]: `ssrRenderClass`,
index e0780fa0c42787633bebf50628200b455644c013..e7d111299f090a32fb282d3a3796aa52dd6c27a3 100644 (file)
@@ -1,16 +1,31 @@
 import {
+  ElementTypes,
   type NodeTransform,
   NodeTypes,
   type SkipNode,
+  createCallExpression,
   createIfStatement,
   createStructuralDirectiveTransform,
   processSkip,
 } from '@vue/compiler-core'
 import { processIfBranch } from './ssrVIf'
 import type { SSRTransformContext } from '../ssrCodegenTransform'
+import { SSR_RENDER_SKIP_COMPONENT } from '../runtimeHelpers'
 
 export const ssrTransformSkip: NodeTransform =
-  createStructuralDirectiveTransform('skip', processSkip)
+  createStructuralDirectiveTransform('skip', (node, dir, context) => {
+    processSkip(node, dir, context)
+    return () => {
+      if (node.tagType === ElementTypes.COMPONENT && node.ssrCodegenNode) {
+        const { arguments: args, loc } = node.ssrCodegenNode
+        node.ssrCodegenNode = createCallExpression(
+          context.helper(SSR_RENDER_SKIP_COMPONENT),
+          [`_push`, dir.exp!, ...args],
+          loc,
+        )
+      }
+    }
+  })
 
 export function ssrProcessSkip(
   node: SkipNode,
index aa6532c28117f3aa37e0ebf9f6ff20c28a6d6189..dc7d13a6dfe287ff9d77794032354eb9464a0f6e 100644 (file)
@@ -9,6 +9,11 @@ import type { Directive } from '../directives'
 import { camelize, capitalize, isString } from '@vue/shared'
 import { warn } from '../warning'
 import type { VNodeTypes } from '../vnode'
+import {
+  type ComponentPublicInstance,
+  defineComponent,
+  renderSlot,
+} from '@vue/runtime-core'
 
 export const COMPONENTS = 'components'
 export const DIRECTIVES = 'directives'
@@ -138,3 +143,21 @@ function resolve(registry: Record<string, any> | undefined, name: string) {
       registry[capitalize(camelize(name))])
   )
 }
+
+let _comp: ConcreteComponent | undefined
+/**
+ * @private
+ */
+export function resolveSkipComponent(
+  isSkip: boolean,
+  Comp: ConcreteComponent,
+): ConcreteComponent {
+  return isSkip
+    ? _comp ||
+        (_comp = defineComponent({
+          render(this: ComponentPublicInstance) {
+            return renderSlot(this.$slots, 'default')
+          },
+        }))
+    : Comp
+}
index 3871167b3eeaf4655d7fe7fd36781a1f3b560a87..2cefe3eda8c5471f743857161e58f5ea172f3102 100644 (file)
@@ -144,6 +144,7 @@ export {
   resolveComponent,
   resolveDirective,
   resolveDynamicComponent,
+  resolveSkipComponent,
 } from './helpers/resolveAssets'
 // For integration with runtime compiler
 export { registerRuntimeCompiler, isRuntimeOnly } from './component'
diff --git a/packages/server-renderer/src/helpers/ssrRenderSkipComponent.ts b/packages/server-renderer/src/helpers/ssrRenderSkipComponent.ts
new file mode 100644 (file)
index 0000000..580cadc
--- /dev/null
@@ -0,0 +1,42 @@
+import {
+  type Component,
+  type ComponentInternalInstance,
+  type Slots,
+  createVNode,
+} from 'vue'
+import {
+  type Props,
+  type PushFn,
+  type SSRBuffer,
+  renderComponentVNode,
+} from '../render'
+import { type SSRSlots, ssrRenderSlot } from './ssrRenderSlot'
+
+export function ssrRenderSkipComponent(
+  push: PushFn,
+  isSkip: boolean,
+  comp: Component,
+  props: Props | null = null,
+  children: Slots | SSRSlots | null = null,
+  parentComponent: ComponentInternalInstance | null = null,
+  slotScopeId?: string,
+): SSRBuffer | Promise<SSRBuffer> {
+  if (isSkip) {
+    // only render default slot without slot props
+    ssrRenderSlot(
+      children!,
+      'default',
+      {},
+      null,
+      push,
+      parentComponent!,
+      slotScopeId,
+    )
+    return []
+  }
+  return renderComponentVNode(
+    createVNode(comp, props, children),
+    parentComponent,
+    slotScopeId,
+  )
+}
index 3a2054066c3c89f7bb0c4310f552217270dcdf0b..744f0a593065cc8a587c35852974b9b3c20ee95b 100644 (file)
@@ -1,6 +1,7 @@
 // internal runtime helpers
 export { renderVNode as ssrRenderVNode } from './render'
 export { ssrRenderComponent } from './helpers/ssrRenderComponent'
+export { ssrRenderSkipComponent } from './helpers/ssrRenderSkipComponent'
 export { ssrRenderSlot, ssrRenderSlotInner } from './helpers/ssrRenderSlot'
 export { ssrRenderTeleport } from './helpers/ssrRenderTeleport'
 export {