From c07c88a6e0f5eae3bb5f3d5d17160fc1a7151dd4 Mon Sep 17 00:00:00 2001
From: 0xfe <2953427626@qq.com>
Date: Tue, 30 Dec 2025 23:32:09 +0800
Subject: [PATCH 1/5] fix(ssr): add support for props modifiers in render
functions
---
.../server-renderer/__tests__/render.spec.ts | 17 +++++++++++++++++
.../src/helpers/ssrRenderAttrs.ts | 16 +++++++++++++++-
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts
index d0a5223b2ff..097350ccc21 100644
--- a/packages/server-renderer/__tests__/render.spec.ts
+++ b/packages/server-renderer/__tests__/render.spec.ts
@@ -1229,5 +1229,22 @@ function testRender(type: string, render: typeof renderToString) {
// during the render phase
expect(getterSpy).toHaveBeenCalledTimes(2)
})
+ test('props modifiers in render functions', async () => {
+ const app = createApp({
+ setup() {
+ return () =>
+ h(
+ 'div',
+ {
+ '^attr': 'attr',
+ '.prop': 'prop',
+ },
+ 'Functional Component',
+ )
+ },
+ })
+ const html = await render(app)
+ expect(html).toBe(`
Functional Component
`)
+ })
})
}
diff --git a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
index b082da03fe8..cbd4c21cc52 100644
--- a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
+++ b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
@@ -24,6 +24,16 @@ const shouldIgnoreProp = /*@__PURE__*/ makeMap(
`,key,ref,innerHTML,textContent,ref_key,ref_for`,
)
+const applyModifiers = (prop: string): string | null => {
+ if (prop.startsWith('^')) {
+ return prop.slice(1) // attribute binding
+ }
+ if (prop.startsWith('.')) {
+ return null // prop should be ignored
+ }
+ return prop // normal prop
+}
+
export function ssrRenderAttrs(
props: Record,
tag?: string,
@@ -45,7 +55,11 @@ export function ssrRenderAttrs(
} else if (key === 'className') {
ret += ` class="${String(value)}"`
} else {
- ret += ssrRenderDynamicAttr(key, value, tag)
+ const modifiedKey = applyModifiers(key)
+ if (modifiedKey === null) {
+ continue
+ }
+ ret += ssrRenderDynamicAttr(modifiedKey, value, tag)
}
}
return ret
From 97035af558b700f03420bc71487065baddaf8ebf Mon Sep 17 00:00:00 2001
From: Gianthard-cyh <45843411+Gianthard-cyh@users.noreply.github.com>
Date: Tue, 30 Dec 2025 23:56:29 +0800
Subject: [PATCH 2/5] Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
packages/server-renderer/src/helpers/ssrRenderAttrs.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
index cbd4c21cc52..fa14425b927 100644
--- a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
+++ b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
@@ -26,10 +26,10 @@ const shouldIgnoreProp = /*@__PURE__*/ makeMap(
const applyModifiers = (prop: string): string | null => {
if (prop.startsWith('^')) {
- return prop.slice(1) // attribute binding
+ return prop.slice(1) // force as attribute
}
if (prop.startsWith('.')) {
- return null // prop should be ignored
+ return null // force as property (not rendered in SSR)
}
return prop // normal prop
}
From c8fb292d034d786b31977c82b753aa1011d8fed7 Mon Sep 17 00:00:00 2001
From: daiwei
Date: Thu, 1 Jan 2026 21:46:43 +0800
Subject: [PATCH 3/5] fix(ssr): simplify attribute modifier handling in
ssrRenderAttrs
---
.../src/helpers/ssrRenderAttrs.ts | 24 ++++++-------------
1 file changed, 7 insertions(+), 17 deletions(-)
diff --git a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
index fa14425b927..b031079e92e 100644
--- a/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
+++ b/packages/server-renderer/src/helpers/ssrRenderAttrs.ts
@@ -24,30 +24,24 @@ const shouldIgnoreProp = /*@__PURE__*/ makeMap(
`,key,ref,innerHTML,textContent,ref_key,ref_for`,
)
-const applyModifiers = (prop: string): string | null => {
- if (prop.startsWith('^')) {
- return prop.slice(1) // force as attribute
- }
- if (prop.startsWith('.')) {
- return null // force as property (not rendered in SSR)
- }
- return prop // normal prop
-}
-
export function ssrRenderAttrs(
props: Record,
tag?: string,
): string {
let ret = ''
- for (const key in props) {
+ for (let key in props) {
if (
shouldIgnoreProp(key) ||
isOn(key) ||
- (tag === 'textarea' && key === 'value')
+ (tag === 'textarea' && key === 'value') ||
+ // force as property (not rendered in SSR)
+ key.startsWith('.')
) {
continue
}
const value = props[key]
+ // force as attribute
+ if (key.startsWith('^')) key = key.slice(1)
if (key === 'class') {
ret += ` class="${ssrRenderClass(value)}"`
} else if (key === 'style') {
@@ -55,11 +49,7 @@ export function ssrRenderAttrs(
} else if (key === 'className') {
ret += ` class="${String(value)}"`
} else {
- const modifiedKey = applyModifiers(key)
- if (modifiedKey === null) {
- continue
- }
- ret += ssrRenderDynamicAttr(modifiedKey, value, tag)
+ ret += ssrRenderDynamicAttr(key, value, tag)
}
}
return ret
From 43a462e34daaf31989395d625a9ced18996b2814 Mon Sep 17 00:00:00 2001
From: daiwei
Date: Thu, 1 Jan 2026 21:47:46 +0800
Subject: [PATCH 4/5] chore: format
---
packages/server-renderer/__tests__/render.spec.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts
index 097350ccc21..b60e406c05d 100644
--- a/packages/server-renderer/__tests__/render.spec.ts
+++ b/packages/server-renderer/__tests__/render.spec.ts
@@ -1229,6 +1229,7 @@ function testRender(type: string, render: typeof renderToString) {
// during the render phase
expect(getterSpy).toHaveBeenCalledTimes(2)
})
+
test('props modifiers in render functions', async () => {
const app = createApp({
setup() {
From 0120fd2148ff6fb522ee2ce3a4adb5d6ae4af42a Mon Sep 17 00:00:00 2001
From: daiwei
Date: Thu, 1 Jan 2026 21:51:08 +0800
Subject: [PATCH 5/5] test(ssr): update test description for props modifiers
---
packages/server-renderer/__tests__/render.spec.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server-renderer/__tests__/render.spec.ts b/packages/server-renderer/__tests__/render.spec.ts
index b60e406c05d..e0136cb5c97 100644
--- a/packages/server-renderer/__tests__/render.spec.ts
+++ b/packages/server-renderer/__tests__/render.spec.ts
@@ -1230,7 +1230,7 @@ function testRender(type: string, render: typeof renderToString) {
expect(getterSpy).toHaveBeenCalledTimes(2)
})
- test('props modifiers in render functions', async () => {
+ test('props modifiers in render attrs', async () => {
const app = createApp({
setup() {
return () =>