]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): handle v-bind modifiers during render attrs (#14263)
authorGianthard-cyh <45843411+Gianthard-cyh@users.noreply.github.com>
Mon, 19 Jan 2026 00:51:14 +0000 (08:51 +0800)
committerGitHub <noreply@github.com>
Mon, 19 Jan 2026 00:51:14 +0000 (08:51 +0800)
close #14262

packages/server-renderer/__tests__/render.spec.ts
packages/server-renderer/src/helpers/ssrRenderAttrs.ts

index d0a5223b2ff635b2df39b187f8f4438b01bfff38..e0136cb5c97515a287a31504232e5ddc08330e24 100644 (file)
@@ -1229,5 +1229,23 @@ function testRender(type: string, render: typeof renderToString) {
       // during the render phase
       expect(getterSpy).toHaveBeenCalledTimes(2)
     })
+
+    test('props modifiers in render attrs', async () => {
+      const app = createApp({
+        setup() {
+          return () =>
+            h(
+              'div',
+              {
+                '^attr': 'attr',
+                '.prop': 'prop',
+              },
+              'Functional Component',
+            )
+        },
+      })
+      const html = await render(app)
+      expect(html).toBe(`<div attr="attr">Functional Component</div>`)
+    })
   })
 }
index 903a9c9b3d4b36a5ba29d4bf87dfd2168aab1de1..4d4bad4a1fa0efa329cbf08dc1ca77e30c1f0300 100644 (file)
@@ -29,15 +29,19 @@ export function ssrRenderAttrs(
   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' || key === 'className') {
       ret += ` class="${ssrRenderClass(value)}"`
     } else if (key === 'style') {