fix(hydration): fix SFC style v-bind hydration mismatch warnings (#10250)
close #10215
This commit is contained in:
parent
f31d782e46
commit
f0b5f7ed8d
|
@ -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()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue