From 977261d4e0c91fe1e6993b8e1e5777352c52282b Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 29 Aug 2024 10:22:24 +0800 Subject: [PATCH] fix(hydration): escape css var name to avoid mismatch --- packages/compiler-sfc/src/script/utils.ts | 12 ----------- packages/compiler-sfc/src/style/cssVars.ts | 2 +- .../runtime-core/__tests__/hydration.spec.ts | 20 +++++++++++++++++++ packages/runtime-core/src/hydration.ts | 6 +++++- packages/shared/src/escapeHtml.ts | 12 +++++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/packages/compiler-sfc/src/script/utils.ts b/packages/compiler-sfc/src/script/utils.ts index f9b4ad68625..39f163c6b77 100644 --- a/packages/compiler-sfc/src/script/utils.ts +++ b/packages/compiler-sfc/src/script/utils.ts @@ -121,15 +121,3 @@ export const propNameEscapeSymbolsRE: RegExp = export function getEscapedPropName(key: string): string { return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key } - -export const cssVarNameEscapeSymbolsRE: RegExp = - /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g - -export function getEscapedCssVarName( - key: string, - doubleEscape: boolean, -): string { - return key.replace(cssVarNameEscapeSymbolsRE, s => - doubleEscape ? `\\\\${s}` : `\\${s}`, - ) -} diff --git a/packages/compiler-sfc/src/style/cssVars.ts b/packages/compiler-sfc/src/style/cssVars.ts index 47bb7c083bb..0397c7d790a 100644 --- a/packages/compiler-sfc/src/style/cssVars.ts +++ b/packages/compiler-sfc/src/style/cssVars.ts @@ -8,9 +8,9 @@ import { processExpression, } from '@vue/compiler-dom' import type { SFCDescriptor } from '../parse' -import { getEscapedCssVarName } from '../script/utils' import type { PluginCreator } from 'postcss' import hash from 'hash-sum' +import { getEscapedCssVarName } from '@vue/shared' export const CSS_VARS_HELPER = `useCssVars` diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 142a171f7cf..45e9118428c 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -2021,6 +2021,26 @@ describe('SSR hydration', () => { app.mount(container) expect(`Hydration style mismatch`).not.toHaveBeenWarned() }) + + test('escape css var name', () => { + const container = document.createElement('div') + container.innerHTML = `
` + const app = createSSRApp({ + setup() { + useCssVars(() => ({ + 'foo.bar': 'red', + })) + return () => h(Child) + }, + }) + const Child = { + setup() { + return () => h('div', { style: 'padding: 4px' }) + }, + } + app.mount(container) + expect(`Hydration style mismatch`).not.toHaveBeenWarned() + }) }) describe('data-allow-mismatch', () => { diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 8060f9475b7..6c63b521cf7 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -18,6 +18,7 @@ import { PatchFlags, ShapeFlags, def, + getEscapedCssVarName, includeBooleanAttr, isBooleanAttr, isKnownHtmlAttr, @@ -916,7 +917,10 @@ function resolveCssVars( ) { const cssVars = instance.getCssVars() for (const key in cssVars) { - expectedMap.set(`--${key}`, String(cssVars[key])) + expectedMap.set( + `--${getEscapedCssVarName(key, false)}`, + String(cssVars[key]), + ) } } if (vnode === root && instance.parent) { diff --git a/packages/shared/src/escapeHtml.ts b/packages/shared/src/escapeHtml.ts index 22ce632eb11..94d07fd4120 100644 --- a/packages/shared/src/escapeHtml.ts +++ b/packages/shared/src/escapeHtml.ts @@ -50,3 +50,15 @@ const commentStripRE = /^-?>||--!>|?@[\\\]^`{|}~]/g + +export function getEscapedCssVarName( + key: string, + doubleEscape: boolean, +): string { + return key.replace(cssVarNameEscapeSymbolsRE, s => + doubleEscape ? `\\\\${s}` : `\\${s}`, + ) +}