From 42239cf2846f50b6ac2c060dad381113840d9ea1 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 10 Nov 2022 14:29:51 +0800 Subject: [PATCH] fix(teleport/css-v-bind): fix css v-bind for teleported content fix #4605 close #4609 (used tests from this PR) --- packages/runtime-core/src/component.ts | 6 ++ .../runtime-core/src/components/Teleport.ts | 20 +++++++ .../__tests__/helpers/useCssVars.spec.ts | 55 +++++++++++++++++++ .../runtime-dom/src/helpers/useCssVars.ts | 15 ++++- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 939d84e3705..3fdf566bb98 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -458,6 +458,12 @@ export interface ComponentInternalInstance { * @internal */ n?: () => Promise + /** + * `updateTeleportCssVars` + * For updating css vars on contained teleports + * @internal + */ + ut?: (vars?: Record) => void } const emptyAppContext = createAppContext() diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index e519aa2bb3a..18c4aee86a0 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -222,6 +222,8 @@ export const TeleportImpl = { } } } + + updateCssVars(parentComponent, n2) }, remove( @@ -383,6 +385,7 @@ function hydrateTeleport( ) } } + updateCssVars(parentComponent, vnode) } return vnode.anchor && nextSibling(vnode.anchor as Node) } @@ -392,3 +395,20 @@ export const Teleport = TeleportImpl as unknown as { __isTeleport: true new (): { $props: VNodeProps & TeleportProps } } + +function updateCssVars( + parentComponent: ComponentInternalInstance | null, + vnode: VNode +) { + // presence of .ut method indicates owner component uses css vars. + // code path here can assume browser environment. + if (parentComponent && parentComponent.ut) { + let node = (vnode.children as VNode[])[0].el! + while (node !== vnode.targetAnchor) { + if (node.nodeType === 1) + node.setAttribute('data-v-owner', parentComponent.uid) + node = node.nextSibling + } + parentComponent.ut() + } +} diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index fb22529f42d..e9dd3659650 100644 --- a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -8,6 +8,7 @@ import { nextTick, ComponentOptions, Suspense, + Teleport, FunctionalComponent } from '@vue/runtime-dom' @@ -196,4 +197,58 @@ describe('useCssVars', () => { expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') } }) + + test('with teleport', async () => { + document.body.innerHTML = '' + const state = reactive({ color: 'red' }) + const root = document.createElement('div') + const target = document.body + + const App = { + setup() { + useCssVars(() => state) + return () => [h(Teleport, { to: target }, [h('div')])] + } + } + + render(h(App), root) + await nextTick() + for (const c of [].slice.call(target.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') + } + }) + + test('with teleport(change subTree)', async () => { + document.body.innerHTML = '' + const state = reactive({ color: 'red' }) + const root = document.createElement('div') + const target = document.body + const toggle = ref(false) + + const App = { + setup() { + useCssVars(() => state) + return () => [ + h(Teleport, { to: target }, [ + h('div'), + toggle.value ? h('div') : null + ]) + ] + } + } + + render(h(App), root) + await nextTick() + expect(target.children.length).toBe(1) + for (const c of [].slice.call(target.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') + } + + toggle.value = true + await nextTick() + expect(target.children.length).toBe(2) + for (const c of [].slice.call(target.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') + } + }) }) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index 510e81b065b..e0a95c9ca99 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -25,9 +25,20 @@ export function useCssVars(getter: (ctx: any) => Record) { return } - const setVars = () => - setVarsOnVNode(instance.subTree, getter(instance.proxy!)) + const updateTeleports = (instance.ut = (vars = getter(instance.proxy)) => { + Array.from( + document.querySelectorAll(`[data-v-owner="${instance.uid}"]`) + ).forEach(node => setVarsOnNode(node, vars)) + }) + + const setVars = () => { + const vars = getter(instance.proxy) + setVarsOnVNode(instance.subTree, vars) + updateTeleports(vars) + } + watchPostEffect(setVars) + onMounted(() => { const ob = new MutationObserver(setVars) ob.observe(instance.subTree.el!.parentNode, { childList: true })