From 2b4aa83fe2858b21be320f11c44d01b964ce2591 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 17 Jun 2025 19:50:24 +0800 Subject: [PATCH 1/2] fix(ssr): handle initial selected state for select with v-model + v-for/v-if option --- .../compiler-ssr/__tests__/ssrVModel.spec.ts | 56 +++++++++++++++++++ .../compiler-ssr/src/transforms/ssrVModel.ts | 14 ++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts index 0bf7673d00d..59eb6de2924 100644 --- a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts @@ -166,6 +166,62 @@ describe('ssr: v-model', () => { _push(\`\`) }" `) + + expect( + compileWithWrapper(` + `).code, + ).toMatchInlineSnapshot(` + "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper(` + `).code, + ).toMatchInlineSnapshot(` + "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) }) test('', () => { diff --git a/packages/compiler-ssr/src/transforms/ssrVModel.ts b/packages/compiler-ssr/src/transforms/ssrVModel.ts index 80e3839318b..52b243b5aa0 100644 --- a/packages/compiler-ssr/src/transforms/ssrVModel.ts +++ b/packages/compiler-ssr/src/transforms/ssrVModel.ts @@ -65,9 +65,17 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { ) } } else if (plainNode.tag === 'optgroup') { - plainNode.children.forEach(option => - processOption(option as PlainElementNode), - ) + plainNode.children.forEach(option => { + if (option.type === NodeTypes.ELEMENT) { + processOption(option as PlainElementNode) + } else if (option.type === NodeTypes.FOR) { + option.children.forEach(c => processOption(c as PlainElementNode)) + } else if (option.type === NodeTypes.IF) { + option.branches.forEach(b => + b.children.forEach(c => processOption(c as PlainElementNode)), + ) + } + }) } } From b1894b930c07fe88959401e24608c8b82f7ef3fe Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 18 Jun 2025 08:22:32 +0800 Subject: [PATCH 2/2] chore: add more cases --- .../compiler-ssr/__tests__/ssrVModel.spec.ts | 70 +++++++++++++++++++ .../compiler-ssr/src/transforms/ssrVModel.ts | 37 ++++------ 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts index 59eb6de2924..8a439dbf4b5 100644 --- a/packages/compiler-ssr/__tests__/ssrVModel.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVModel.spec.ts @@ -222,6 +222,76 @@ describe('ssr: v-model', () => { _push(\`\`) }" `) + + expect( + compileWithWrapper(` + `).code, + ).toMatchInlineSnapshot(` + "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) + + expect( + compileWithWrapper(` + `).code, + ).toMatchInlineSnapshot(` + "const { ssrRenderAttr: _ssrRenderAttr, ssrIncludeBooleanAttr: _ssrIncludeBooleanAttr, ssrLooseContain: _ssrLooseContain, ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require("vue/server-renderer") + + return function ssrRender(_ctx, _push, _parent, _attrs) { + _push(\`\`) + }" + `) }) test('', () => { diff --git a/packages/compiler-ssr/src/transforms/ssrVModel.ts b/packages/compiler-ssr/src/transforms/ssrVModel.ts index 52b243b5aa0..cbe5b2b42a3 100644 --- a/packages/compiler-ssr/src/transforms/ssrVModel.ts +++ b/packages/compiler-ssr/src/transforms/ssrVModel.ts @@ -39,6 +39,18 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { } } + const processSelectChildren = (children: TemplateChildNode[]) => { + children.forEach(child => { + if (child.type === NodeTypes.ELEMENT) { + processOption(child as PlainElementNode) + } else if (child.type === NodeTypes.FOR) { + processSelectChildren(child.children) + } else if (child.type === NodeTypes.IF) { + child.branches.forEach(b => processSelectChildren(b.children)) + } + }) + } + function processOption(plainNode: PlainElementNode) { if (plainNode.tag === 'option') { if (plainNode.props.findIndex(p => p.name === 'selected') === -1) { @@ -65,17 +77,7 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { ) } } else if (plainNode.tag === 'optgroup') { - plainNode.children.forEach(option => { - if (option.type === NodeTypes.ELEMENT) { - processOption(option as PlainElementNode) - } else if (option.type === NodeTypes.FOR) { - option.children.forEach(c => processOption(c as PlainElementNode)) - } else if (option.type === NodeTypes.IF) { - option.branches.forEach(b => - b.children.forEach(c => processOption(c as PlainElementNode)), - ) - } - }) + processSelectChildren(plainNode.children) } } @@ -171,18 +173,7 @@ export const ssrTransformModel: DirectiveTransform = (dir, node, context) => { checkDuplicatedValue() node.children = [createInterpolation(model, model.loc)] } else if (node.tag === 'select') { - const processChildren = (children: TemplateChildNode[]) => { - children.forEach(child => { - if (child.type === NodeTypes.ELEMENT) { - processOption(child as PlainElementNode) - } else if (child.type === NodeTypes.FOR) { - processChildren(child.children) - } else if (child.type === NodeTypes.IF) { - child.branches.forEach(b => processChildren(b.children)) - } - }) - } - processChildren(node.children) + processSelectChildren(node.children) } else { context.onError( createDOMCompilerError(