Skip to content

Commit ca12e77

Browse files
authoredSep 3, 2024··
fix(hydration): escape css var name to avoid mismatch (#11739)
close #11735
1 parent cb843e0 commit ca12e77

File tree

5 files changed

+38
-14
lines changed

5 files changed

+38
-14
lines changed
 

‎packages/compiler-sfc/src/script/utils.ts

-12
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,3 @@ export const propNameEscapeSymbolsRE: RegExp =
121121
export function getEscapedPropName(key: string): string {
122122
return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key
123123
}
124-
125-
export const cssVarNameEscapeSymbolsRE: RegExp =
126-
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
127-
128-
export function getEscapedCssVarName(
129-
key: string,
130-
doubleEscape: boolean,
131-
): string {
132-
return key.replace(cssVarNameEscapeSymbolsRE, s =>
133-
doubleEscape ? `\\\\${s}` : `\\${s}`,
134-
)
135-
}

‎packages/compiler-sfc/src/style/cssVars.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import {
88
processExpression,
99
} from '@vue/compiler-dom'
1010
import type { SFCDescriptor } from '../parse'
11-
import { getEscapedCssVarName } from '../script/utils'
1211
import type { PluginCreator } from 'postcss'
1312
import hash from 'hash-sum'
13+
import { getEscapedCssVarName } from '@vue/shared'
1414

1515
export const CSS_VARS_HELPER = `useCssVars`
1616

‎packages/runtime-core/__tests__/hydration.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -2021,6 +2021,26 @@ describe('SSR hydration', () => {
20212021
app.mount(container)
20222022
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
20232023
})
2024+
2025+
test('escape css var name', () => {
2026+
const container = document.createElement('div')
2027+
container.innerHTML = `<div style="padding: 4px;--foo\\.bar:red;"></div>`
2028+
const app = createSSRApp({
2029+
setup() {
2030+
useCssVars(() => ({
2031+
'foo.bar': 'red',
2032+
}))
2033+
return () => h(Child)
2034+
},
2035+
})
2036+
const Child = {
2037+
setup() {
2038+
return () => h('div', { style: 'padding: 4px' })
2039+
},
2040+
}
2041+
app.mount(container)
2042+
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
2043+
})
20242044
})
20252045

20262046
describe('data-allow-mismatch', () => {

‎packages/runtime-core/src/hydration.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
PatchFlags,
1919
ShapeFlags,
2020
def,
21+
getEscapedCssVarName,
2122
includeBooleanAttr,
2223
isBooleanAttr,
2324
isKnownHtmlAttr,
@@ -915,7 +916,10 @@ function resolveCssVars(
915916
) {
916917
const cssVars = instance.getCssVars()
917918
for (const key in cssVars) {
918-
expectedMap.set(`--${key}`, String(cssVars[key]))
919+
expectedMap.set(
920+
`--${getEscapedCssVarName(key, false)}`,
921+
String(cssVars[key]),
922+
)
919923
}
920924
}
921925
if (vnode === root && instance.parent) {

‎packages/shared/src/escapeHtml.ts

+12
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,15 @@ const commentStripRE = /^-?>|<!--|-->|--!>|<!-$/g
5050
export function escapeHtmlComment(src: string): string {
5151
return src.replace(commentStripRE, '')
5252
}
53+
54+
export const cssVarNameEscapeSymbolsRE: RegExp =
55+
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
56+
57+
export function getEscapedCssVarName(
58+
key: string,
59+
doubleEscape: boolean,
60+
): string {
61+
return key.replace(cssVarNameEscapeSymbolsRE, s =>
62+
doubleEscape ? `\\\\${s}` : `\\${s}`,
63+
)
64+
}

0 commit comments

Comments
 (0)
Please sign in to comment.