diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index f191c36df12..9281642715f 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -585,8 +585,10 @@ export interface ComponentInternalInstance { * For updating css vars on contained teleports * @internal */ - ut?: (vars?: Record) => void - + ut?: { + uid: number + update: (vars?: Record) => void + }[] /** * dev only. For style v-bind hydration mismatch checks * @internal diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index c37356a7869..133c5fa9271 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -489,20 +489,24 @@ function updateCssVars(vnode: VNode, isDisabled: boolean) { // presence of .ut method indicates owner component uses css vars. // code path here can assume browser environment. const ctx = vnode.ctx + let node, anchor + if (isDisabled) { + node = vnode.el + anchor = vnode.anchor + } else { + node = vnode.targetStart + anchor = vnode.targetAnchor + } if (ctx && ctx.ut) { - let node, anchor - if (isDisabled) { - node = vnode.el - anchor = vnode.anchor - } else { - node = vnode.targetStart - anchor = vnode.targetAnchor - } - while (node && node !== anchor) { - if (node.nodeType === 1) node.setAttribute('data-v-owner', ctx.uid) - node = node.nextSibling - } - ctx.ut() + ctx.ut.forEach(i => { + let currentNode = node + while (currentNode && currentNode !== anchor) { + if (currentNode.nodeType === 1) + currentNode.setAttribute(`data-v-owner-${i.uid}`, '') + currentNode = currentNode.nextSibling + } + i.update() + }) } } diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index a8c5340cd1f..2c97ca47177 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -491,6 +491,8 @@ function createBaseVNode( ctx: currentRenderingInstance, } as VNode + updateUt(vnode) + if (needFullChildrenNormalization) { normalizeChildren(vnode, children) // normalize suspense children @@ -537,6 +539,19 @@ function createBaseVNode( return vnode } +function updateUt(vnode: VNode) { + if (vnode.ctx && vnode.ctx.parent && vnode.ctx.parent.ut) { + const parentUt = vnode.ctx.parent.ut.slice(0) + if (vnode.shapeFlag & ShapeFlags.COMPONENT) { + if (vnode.ctx.ut) { + vnode.ctx.ut.unshift(...parentUt) + } else vnode.ctx.ut = parentUt + } else if (vnode.shapeFlag & ShapeFlags.TELEPORT) { + if (!vnode.ctx.ut) vnode.ctx.ut = parentUt + } + } +} + export { createBaseVNode as createElementVNode } export const createVNode = ( diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index 1fb4cc65fd0..4a5099d23ee 100644 --- a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -276,7 +276,38 @@ describe('useCssVars', () => { expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') } }) + test('with nested teleport', async () => { + document.body.innerHTML = '' + const state = { color: 'red' } + const root = document.createElement('div') + const target = document.body + const App = { + setup() { + useCssVars(() => state) + return () => h(Comp) + }, + } + const Comp = { + setup() { + return () => h(NestedTeleport) + }, + } + const NestedTeleport = { + setup() { + return () => + h( + Teleport, + { to: target }, + h('div', { class: 'color' }, 'Another teleport'), + ) + }, + } + render(h(App), root) + await nextTick() + const dom: HTMLElement = target.querySelector('.color')! + expect(dom.style.getPropertyValue(`--color`)).toBe('red') + }) test('with teleport in child slot', async () => { document.body.innerHTML = '' const state = reactive({ color: 'red' }) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index e2bc6de9278..b3d412cdecb 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -29,11 +29,18 @@ export function useCssVars(getter: (ctx: any) => Record): void { } /* v8 ignore stop */ - const updateTeleports = (instance.ut = (vars = getter(instance.proxy)) => { - Array.from( - document.querySelectorAll(`[data-v-owner="${instance.uid}"]`), - ).forEach(node => setVarsOnNode(node, vars)) - }) + const ut = { + uid: instance.uid, + update: (vars = getter(instance.proxy)) => { + Array.from( + document.querySelectorAll(`[data-v-owner-${instance.uid}]`), + ).forEach(node => setVarsOnNode(node, vars)) + }, + } + instance.ut = [ut] + const updateTeleports = () => { + instance.ut!.forEach(i => i.update()) + } if (__DEV__) { instance.getCssVars = () => getter(instance.proxy) @@ -46,7 +53,7 @@ export function useCssVars(getter: (ctx: any) => Record): void { } else { setVarsOnVNode(instance.subTree, vars) } - updateTeleports(vars) + updateTeleports() } // handle cases where child component root is affected