From: daiwei Date: Fri, 24 Jan 2025 09:26:43 +0000 (+0800) Subject: refactor: tweak v-skip on component X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=feda7a09a8f751c95c77323b2ec734b576f3dd20;p=thirdparty%2Fvuejs%2Fcore.git refactor: tweak v-skip on component --- diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap index bb7cdf978f..8904400a65 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSkip.spec.ts.snap @@ -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 diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts index 7cf3757b24..46b36f4505 100644 --- a/packages/compiler-core/src/runtimeHelpers.ts +++ b/packages/compiler-core/src/runtimeHelpers.ts @@ -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 = { [CREATE_STATIC]: `createStaticVNode`, [RESOLVE_COMPONENT]: `resolveComponent`, [RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`, + [RESOLVE_SKIP_COMPONENT]: `resolveSkipComponent`, [RESOLVE_DIRECTIVE]: `resolveDirective`, [RESOLVE_FILTER]: `resolveFilter`, [WITH_DIRECTIVES]: `withDirectives`, diff --git a/packages/compiler-core/src/transforms/vSkip.ts b/packages/compiler-core/src/transforms/vSkip.ts index e9582e8e5a..cd04c8db89 100644 --- a/packages/compiler-core/src/transforms/vSkip.ts +++ b/packages/compiler-core/src/transforms/vSkip.ts @@ -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, + ]) +} diff --git a/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts b/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts index 3c1c8cfc02..4929912b6f 100644 --- a/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts @@ -195,45 +195,37 @@ describe('ssr: v-skip', () => { test('on component', () => { expect(compile(``).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(`foo`).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', () => { `, ).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', () => { `, ).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(\`
\`) - } 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(\`\`) - } 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(\`\`) + } else { + return [ + _createVNode("span"), + _createVNode("div") + ] + } + }), + _: 1 /* STABLE */ + }, _parent)) }" `) }) @@ -340,26 +324,22 @@ describe('ssr: v-skip', () => { `, ).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', () => { `).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(\`
\`) - if (_ctx.ok) { - _push(\`\`) - } else { - _push(_ssrRenderComponent(_component_Comp, null, { - default: _withCtx((_, _push, _parent, _scopeId) => { - if (_push) { - _push(\`\`) - } else { - return [ - _createVNode("span") - ] - } - }), - _: 1 /* STABLE */ - }, _parent)) - } - _push(\`\`) - }" - `) + _push(\`
\`) + _push(_ssrRenderSkipComponent(_push, _ctx.ok, _component_Comp, null, { + default: _withCtx((_, _push, _parent, _scopeId) => { + if (_push) { + _push(\`\`) + } else { + return [ + _createVNode("span") + ] + } + }), + _: 1 /* STABLE */ + }, _parent)) + _push(\`\`) + }" + `) }) }) diff --git a/packages/compiler-ssr/src/runtimeHelpers.ts b/packages/compiler-ssr/src/runtimeHelpers.ts index 0e2c8c67be..bc1e9e44e9 100644 --- a/packages/compiler-ssr/src/runtimeHelpers.ts +++ b/packages/compiler-ssr/src/runtimeHelpers.ts @@ -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 = { [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`, diff --git a/packages/compiler-ssr/src/transforms/ssrVSkip.ts b/packages/compiler-ssr/src/transforms/ssrVSkip.ts index e0780fa0c4..e7d111299f 100644 --- a/packages/compiler-ssr/src/transforms/ssrVSkip.ts +++ b/packages/compiler-ssr/src/transforms/ssrVSkip.ts @@ -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, diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts index aa6532c281..dc7d13a6df 100644 --- a/packages/runtime-core/src/helpers/resolveAssets.ts +++ b/packages/runtime-core/src/helpers/resolveAssets.ts @@ -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 | 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 +} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 3871167b3e..2cefe3eda8 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -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 index 0000000000..580cadc058 --- /dev/null +++ b/packages/server-renderer/src/helpers/ssrRenderSkipComponent.ts @@ -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 { + 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, + ) +} diff --git a/packages/server-renderer/src/internal.ts b/packages/server-renderer/src/internal.ts index 3a2054066c..744f0a5930 100644 --- a/packages/server-renderer/src/internal.ts +++ b/packages/server-renderer/src/internal.ts @@ -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 {