fix(runtime-dom): set css vars on update to handle child forcing reflow in onMount (#11561)

This commit is contained in:
linzhe 2024-11-14 15:58:28 +08:00 committed by GitHub
parent 2d5c5e25e9
commit c4312f9c71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 0 deletions

View File

@ -385,6 +385,44 @@ describe('useCssVars', () => {
} }
}) })
test('with delay mount child', async () => {
const state = reactive({ color: 'red' })
const value = ref(false)
const root = document.createElement('div')
const Child = {
setup() {
onMounted(() => {
const childEl = root.children[0]
expect(getComputedStyle(childEl!).getPropertyValue(`--color`)).toBe(
`red`,
)
})
return () => h('div', { id: 'childId' })
},
}
const App = {
setup() {
useCssVars(() => state)
return () => (value.value ? h(Child) : [h('span')])
},
}
render(h(App), root)
await nextTick()
// css vars use with fallback tree
for (const c of [].slice.call(root.children as any)) {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe(`red`)
}
// mount child
value.value = true
await nextTick()
for (const c of [].slice.call(root.children as any)) {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe(`red`)
}
})
// #8826 // #8826
test('with custom element', async () => { test('with custom element', async () => {
const state = reactive({ color: 'red' }) const state = reactive({ color: 'red' })

View File

@ -3,8 +3,10 @@ import {
Static, Static,
type VNode, type VNode,
getCurrentInstance, getCurrentInstance,
onBeforeUpdate,
onMounted, onMounted,
onUnmounted, onUnmounted,
queuePostFlushCb,
warn, warn,
watch, watch,
} from '@vue/runtime-core' } from '@vue/runtime-core'
@ -47,6 +49,12 @@ export function useCssVars(getter: (ctx: any) => Record<string, string>): void {
updateTeleports(vars) updateTeleports(vars)
} }
// handle cases where child component root is affected
// and triggers reflow in onMounted
onBeforeUpdate(() => {
queuePostFlushCb(setVars)
})
onMounted(() => { onMounted(() => {
// run setVars synchronously here, but run as post-effect on changes // run setVars synchronously here, but run as post-effect on changes
watch(setVars, NOOP, { flush: 'post' }) watch(setVars, NOOP, { flush: 'post' })