diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index a72d0a9c2df..2ece460fd7a 100644 --- a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -7,7 +7,8 @@ import { reactive, nextTick, ComponentOptions, - Suspense + Suspense, + FunctionalComponent } from '@vue/runtime-dom' describe('useCssVars', () => { @@ -142,6 +143,40 @@ describe('useCssVars', () => { } }) + // #3894 + test('with subTree change inside HOC', async () => { + const state = reactive({ color: 'red' }) + const value = ref(true) + const root = document.createElement('div') + + const Child: FunctionalComponent = (_, { slots }) => slots.default!() + + const App = { + setup() { + useCssVars(() => state) + return () => + h( + Child, + null, + () => (value.value ? [h('div')] : [h('div'), h('div')]) + ) + } + } + + 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`) + } + + value.value = false + await nextTick() + for (const c of [].slice.call(root.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') + } + }) + test('with createStaticVNode', async () => { const state = reactive({ color: 'red' }) const root = document.createElement('div') diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index 3253dd22731..510e81b065b 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -1,12 +1,12 @@ import { getCurrentInstance, - onMounted, warn, VNode, Fragment, Static, - onUpdated, - watchEffect + watchPostEffect, + onMounted, + onUnmounted } from '@vue/runtime-core' import { ShapeFlags } from '@vue/shared' @@ -27,8 +27,12 @@ export function useCssVars(getter: (ctx: any) => Record) { const setVars = () => setVarsOnVNode(instance.subTree, getter(instance.proxy!)) - onMounted(() => watchEffect(setVars, { flush: 'post' })) - onUpdated(setVars) + watchPostEffect(setVars) + onMounted(() => { + const ob = new MutationObserver(setVars) + ob.observe(instance.subTree.el!.parentNode, { childList: true }) + onUnmounted(() => ob.disconnect()) + }) } function setVarsOnVNode(vnode: VNode, vars: Record) {