fix(hydration): fix SFC style v-bind hydration mismatch warnings (#10250)

close #10215
This commit is contained in:
yangxiuxiu 2024-02-06 17:38:41 +08:00 committed by GitHub
parent f31d782e46
commit f0b5f7ed8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 1 deletions

View File

@ -19,6 +19,7 @@ import {
onMounted, onMounted,
ref, ref,
renderSlot, renderSlot,
useCssVars,
vModelCheckbox, vModelCheckbox,
vShow, vShow,
withDirectives, withDirectives,
@ -1538,5 +1539,20 @@ describe('SSR hydration', () => {
) )
expect(`Hydration attribute mismatch`).not.toHaveBeenWarned() expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
}) })
test('should not warn css v-bind', () => {
const container = document.createElement('div')
container.innerHTML = `<div style="--foo:red;color:var(--foo);" />`
const app = createSSRApp({
setup() {
useCssVars(() => ({
foo: 'red',
}))
return () => h('div', { style: { color: 'var(--foo)' } })
},
})
app.mount(container)
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
})
}) })
}) })

View File

@ -519,6 +519,12 @@ export interface ComponentInternalInstance {
* @internal * @internal
*/ */
ut?: (vars?: Record<string, string>) => void ut?: (vars?: Record<string, string>) => void
/**
* dev only. For style v-bind hydration mismatch checks
* @internal
*/
getCssVars?: () => Record<string, string>
} }
const emptyAppContext = createAppContext() const emptyAppContext = createAppContext()

View File

@ -449,7 +449,10 @@ export function createHydrationFunctions(
) { ) {
for (const key in props) { for (const key in props) {
// check hydration mismatch // check hydration mismatch
if (__DEV__ && propHasMismatch(el, key, props[key], vnode)) { if (
__DEV__ &&
propHasMismatch(el, key, props[key], vnode, parentComponent)
) {
hasMismatch = true hasMismatch = true
} }
if ( if (
@ -718,6 +721,7 @@ function propHasMismatch(
key: string, key: string,
clientValue: any, clientValue: any,
vnode: VNode, vnode: VNode,
instance: ComponentInternalInstance | null,
): boolean { ): boolean {
let mismatchType: string | undefined let mismatchType: string | undefined
let mismatchKey: string | undefined let mismatchKey: string | undefined
@ -748,6 +752,12 @@ function propHasMismatch(
} }
} }
} }
const cssVars = instance?.getCssVars?.()
for (const key in cssVars) {
expectedMap.set(`--${key}`, String(cssVars[key]))
}
if (!isMapEqual(actualMap, expectedMap)) { if (!isMapEqual(actualMap, expectedMap)) {
mismatchType = mismatchKey = 'style' mismatchType = mismatchKey = 'style'
} }

View File

@ -32,6 +32,10 @@ export function useCssVars(getter: (ctx: any) => Record<string, string>) {
).forEach(node => setVarsOnNode(node, vars)) ).forEach(node => setVarsOnNode(node, vars))
}) })
if (__DEV__) {
instance.getCssVars = () => getter(instance.proxy)
}
const setVars = () => { const setVars = () => {
const vars = getter(instance.proxy) const vars = getter(instance.proxy)
setVarsOnVNode(instance.subTree, vars) setVarsOnVNode(instance.subTree, vars)