diff --git a/packages/compiler-ssr/__tests__/ssrVShow.spec.ts b/packages/compiler-ssr/__tests__/ssrVShow.spec.ts index d0f3ec93036..bf8fd6090a2 100644 --- a/packages/compiler-ssr/__tests__/ssrVShow.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVShow.spec.ts @@ -33,6 +33,44 @@ describe('ssr: v-show', () => { `) }) + test('with component', () => { + expect( + compileWithWrapper(``).code, + ).toMatchInlineSnapshot(` + "const { resolveComponent: _resolveComponent } = require("vue") + const { ssrRenderComponent: _ssrRenderComponent, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + const _component_Foo = _resolveComponent("Foo") + + _push(\`\`) + _push(_ssrRenderComponent(_component_Foo, { style: {color:'red'} }, null, _parent, undefined, { + style: (_ctx.foo) ? null : { display: "none" } + })) + _push(\`\`) + }" + `) + }) + + test('with dynamic component', () => { + expect( + compileWithWrapper( + ``, + ).code, + ).toMatchInlineSnapshot(` + "const { resolveDynamicComponent: _resolveDynamicComponent, createVNode: _createVNode } = require("vue") + const { ssrRenderVNode: _ssrRenderVNode, ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent("Foo"), { style: {color:'red'} }, null), _parent, undefined, { + style: (_ctx.foo) ? null : { display: "none" } + }) + _push(\`\`) + }" + `) + }) + test('with static style', () => { expect(compileWithWrapper(`
`).code) .toMatchInlineSnapshot(` diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index cad1ee81028..6b730088e06 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -30,6 +30,7 @@ import { createCallExpression, createFunctionExpression, createIfStatement, + createObjectExpression, createReturnStatement, createRoot, createSimpleExpression, @@ -134,6 +135,17 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { }) } + let vShowExp + const vShowDir = node.props.find( + p => p.type === NodeTypes.DIRECTIVE && p.name === 'show', + ) as DirectiveNode | undefined + if (vShowDir) { + node.props = node.props.filter(p => p !== vShowDir) + const directiveTransform = context.directiveTransforms['show'] + const { props } = directiveTransform!(vShowDir, node, context) + vShowExp = createObjectExpression(props) + } + let propsExp: string | JSChildNode = `null` if (node.props.length) { // note we are not passing ssr: true here because for components, v-on @@ -180,22 +192,31 @@ export const ssrTransformComponent: NodeTransform = (node, context) => { // dynamic component that resolved to a `resolveDynamicComponent` call // expression - since the resolved result may be a plain element (string) // or a VNode, handle it with `renderVNode`. + const args: (string | JSChildNode)[] = [ + `_push`, + createCallExpression(context.helper(CREATE_VNODE), [ + component, + propsExp, + slots, + ]), + `_parent`, + ] + if (vShowExp) args.push(`undefined`, vShowExp) node.ssrCodegenNode = createCallExpression( context.helper(SSR_RENDER_VNODE), - [ - `_push`, - createCallExpression(context.helper(CREATE_VNODE), [ - component, - propsExp, - slots, - ]), - `_parent`, - ], + args, ) } else { + const args: (string | JSChildNode)[] = [ + component, + propsExp, + slots, + `_parent`, + ] + if (vShowExp) args.push(`undefined`, vShowExp) node.ssrCodegenNode = createCallExpression( context.helper(SSR_RENDER_COMPONENT), - [component, propsExp, slots, `_parent`], + args, ) } } diff --git a/packages/server-renderer/__tests__/ssrDirectives.spec.ts b/packages/server-renderer/__tests__/ssrDirectives.spec.ts index dfdebe971f5..f0a947f49bd 100644 --- a/packages/server-renderer/__tests__/ssrDirectives.spec.ts +++ b/packages/server-renderer/__tests__/ssrDirectives.spec.ts @@ -66,6 +66,38 @@ describe('ssr: directives', () => { ), ).toBe(`
`) }) + + test('with component', async () => { + expect( + await renderToString( + createApp({ + components: { + Foo: { + template: `
`, + }, + }, + data: () => ({ show: false }), + template: ``, + }), + ), + ).toBe(`
`) + }) + + test('with dynamic component', async () => { + expect( + await renderToString( + createApp({ + components: { + Foo: { + template: `
`, + }, + }, + data: () => ({ show: false }), + template: ``, + }), + ), + ).toBe(`
`) + }) }) describe('template v-model', () => { diff --git a/packages/server-renderer/src/helpers/ssrRenderComponent.ts b/packages/server-renderer/src/helpers/ssrRenderComponent.ts index 4277bb1747e..94ec2d50f77 100644 --- a/packages/server-renderer/src/helpers/ssrRenderComponent.ts +++ b/packages/server-renderer/src/helpers/ssrRenderComponent.ts @@ -13,10 +13,12 @@ export function ssrRenderComponent( children: Slots | SSRSlots | null = null, parentComponent: ComponentInternalInstance | null = null, slotScopeId?: string, + vShowValue?: Props, ): SSRBuffer | Promise { return renderComponentVNode( createVNode(comp, props, children), parentComponent, slotScopeId, + vShowValue, ) } diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index f04080b9c31..af95a2fbc6e 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -92,6 +92,7 @@ export function renderComponentVNode( vnode: VNode, parentComponent: ComponentInternalInstance | null = null, slotScopeId?: string, + vShowValue?: Props, ): SSRBuffer | Promise { const instance = (vnode.component = createComponentInstance( vnode, @@ -116,15 +117,18 @@ export function renderComponentVNode( }) // Note: error display is already done by the wrapped lifecycle hook function. .catch(NOOP) - return p.then(() => renderComponentSubTree(instance, slotScopeId)) + return p.then(() => + renderComponentSubTree(instance, slotScopeId, vShowValue), + ) } else { - return renderComponentSubTree(instance, slotScopeId) + return renderComponentSubTree(instance, slotScopeId, vShowValue) } } function renderComponentSubTree( instance: ComponentInternalInstance, slotScopeId?: string, + vShowValue?: Props, ): SSRBuffer | Promise { if (__DEV__) pushWarningContext(instance.vnode) const comp = instance.type as Component @@ -186,6 +190,12 @@ function renderComponentSubTree( } } + if (vShowValue) { + // v-show has higher priority + if (attrs) attrs = mergeProps(attrs, vShowValue) + else attrs = vShowValue + } + // set current rendering instance for asset resolution const prev = setCurrentRenderingInstance(instance) try { @@ -225,6 +235,7 @@ export function renderVNode( vnode: VNode, parentComponent: ComponentInternalInstance, slotScopeId?: string, + vShowValue?: Props, ): void { const { type, shapeFlag, children, dirs, props } = vnode if (dirs) { @@ -263,7 +274,9 @@ export function renderVNode( if (shapeFlag & ShapeFlags.ELEMENT) { renderElementVNode(push, vnode, parentComponent, slotScopeId) } else if (shapeFlag & ShapeFlags.COMPONENT) { - push(renderComponentVNode(vnode, parentComponent, slotScopeId)) + push( + renderComponentVNode(vnode, parentComponent, slotScopeId, vShowValue), + ) } else if (shapeFlag & ShapeFlags.TELEPORT) { renderTeleportVNode(push, vnode, parentComponent, slotScopeId) } else if (shapeFlag & ShapeFlags.SUSPENSE) {