From 80b32bf20acddb10aa7541fccc92c3baa03bf408 Mon Sep 17 00:00:00 2001 From: daiwei Date: Sun, 26 Jan 2025 16:02:53 +0800 Subject: [PATCH] wip: fix on component slot + v-if in ssr --- .../compiler-core/src/transforms/vSkip.ts | 8 +++- .../compiler-ssr/__tests__/ssrVSkip.spec.ts | 37 ++++++++++++++++++- .../src/transforms/ssrTransformComponent.ts | 16 +------- packages/shared/src/general.ts | 14 +++++++ 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/packages/compiler-core/src/transforms/vSkip.ts b/packages/compiler-core/src/transforms/vSkip.ts index 3495a00b104..b9e5bcf7de4 100644 --- a/packages/compiler-core/src/transforms/vSkip.ts +++ b/packages/compiler-core/src/transforms/vSkip.ts @@ -32,6 +32,7 @@ import { import { createCodegenNodeForBranch } from './vIf' import { validateBrowserExpression } from '../validateExpression' import { cloneLoc } from '../parser' +import { clone } from '@vue/shared' export const transformSkip: NodeTransform = createStructuralDirectiveTransform( 'skip', @@ -134,7 +135,7 @@ export function processSkip( undefined, true, ) - // find default slot if not has dynamic slots + // find default slot without slot props if not has dynamic slots if (!hasDynamicSlots && slots.type === NodeTypes.JS_OBJECT_EXPRESSION) { processAsSkipNode = true const prop = slots.properties.find( @@ -145,7 +146,10 @@ export function processSkip( p.value.params === undefined, ) if (prop) { - children = prop.value.returns as TemplateChildNode[] + const slotNode = prop.value.returns as TemplateChildNode[] + // clone the slot node to avoid mutating the original one, since it + // will be transformed again in ssr slot vnode fallback + children = context.inSSR ? clone(slotNode) : slotNode } else { context.onError( createCompilerError(ErrorCodes.X_V_SKIP_UNEXPECTED_SLOT, loc), diff --git a/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts b/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts index 23ba0c7f04c..2a177711bbc 100644 --- a/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVSkip.spec.ts @@ -368,7 +368,7 @@ describe('ssr: v-skip', () => { `) }) - test.todo('on component with implicit default slot + v-if', () => { + test('on component with implicit default slot + v-if', () => { expect( compile( ` @@ -376,6 +376,41 @@ describe('ssr: v-skip', () => { `, ).code, ).toMatchInlineSnapshot(` + "const { withCtx: _withCtx, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = require("vue") + const { ssrRenderComponent: _ssrRenderComponent } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + const _component_Comp = _resolveComponent("Comp") + + if (_ctx.ok) { + _push(\`\`) + if (_ctx.yes) { + _push(\`default\`) + } else { + _push(\`\`) + } + _push(\`\`) + } else { + _push(_ssrRenderComponent(_component_Comp, _attrs, { + default: _withCtx((_, _push, _parent, _scopeId) => { + if (_push) { + if (_ctx.yes) { + _push(\`default\`) + } else { + _push(\`\`) + } + } else { + return [ + (_ctx.yes) + ? (_openBlock(), _createBlock("span", { key: 0 }, "default")) + : _createCommentVNode("v-if", true) + ] + } + }), + _: 1 /* STABLE */ + }, _parent)) + } + }" `) }) diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index cad1ee81028..867d61d9605 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -55,7 +55,7 @@ import { ssrProcessTransitionGroup, ssrTransformTransitionGroup, } from './ssrTransformTransitionGroup' -import { extend, isArray, isObject, isPlainObject, isSymbol } from '@vue/shared' +import { clone, extend, isObject, isSymbol } from '@vue/shared' import { buildSSRProps } from './ssrTransformElement' import { ssrProcessTransition, @@ -370,17 +370,3 @@ function subTransform( // node/client branches // - hoists are not enabled for the client branch here } - -function clone(v: any): any { - if (isArray(v)) { - return v.map(clone) - } else if (isPlainObject(v)) { - const res: any = {} - for (const key in v) { - res[key] = clone(v[key as keyof typeof v]) - } - return res - } else { - return v - } -} diff --git a/packages/shared/src/general.ts b/packages/shared/src/general.ts index d5b5e0b942d..ee8e98564db 100644 --- a/packages/shared/src/general.ts +++ b/packages/shared/src/general.ts @@ -217,3 +217,17 @@ export function genCacheKey(source: string, options: any): string { ) ) } + +export function clone(v: any): any { + if (isArray(v)) { + return v.map(clone) + } else if (isPlainObject(v)) { + const res: any = {} + for (const key in v) { + res[key] = clone(v[key as keyof typeof v]) + } + return res + } else { + return v + } +}