From 76e719ba53f8e16906ecc1e50bd36df4d013b8ac Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 16 Sep 2021 19:11:15 +0800 Subject: [PATCH 1/4] fix(cssVars): cssVars work with Teleport --- packages/runtime-dom/src/helpers/useCssVars.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index 510e81b065b..a5ac6a0dda6 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -53,7 +53,7 @@ function setVarsOnVNode(vnode: VNode, vars: Record) { if (vnode.shapeFlag & ShapeFlags.ELEMENT && vnode.el) { setVarsOnNode(vnode.el as Node, vars) - } else if (vnode.type === Fragment) { + } else if (vnode.type === Fragment || vnode.shapeFlag & ShapeFlags.TELEPORT) { ;(vnode.children as VNode[]).forEach(c => setVarsOnVNode(c, vars)) } else if (vnode.type === Static) { let { el, anchor } = vnode From 8f6c1e01c8e65e378475db5e703acc2d03e710b6 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 16 Sep 2021 19:27:30 +0800 Subject: [PATCH 2/4] test: add test --- .../__tests__/helpers/useCssVars.spec.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index fb22529f42d..93f972e99cb 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,23 @@ describe('useCssVars', () => { expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') } }) + + test('with teleport', async () => { + const state = reactive({ color: 'red' }) + const root = document.createElement('div') + const target = document.createElement('div') + + 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') + } + }) }) From 60f20f6eb35bdf52d5fcba9cbc357f7e1d28bcf0 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 17 Sep 2021 21:08:12 +0800 Subject: [PATCH 3/4] chore: improve code --- .../runtime-core/src/components/Teleport.ts | 2 +- packages/runtime-core/src/index.ts | 2 +- .../__tests__/helpers/useCssVars.spec.ts | 33 ++++++++++++ .../runtime-dom/src/helpers/useCssVars.ts | 51 +++++++++++++++++-- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 68d50a63fbd..4b15099f354 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -28,7 +28,7 @@ const isTeleportDisabled = (props: VNode['props']): boolean => const isTargetSVG = (target: RendererElement): boolean => typeof SVGElement !== 'undefined' && target instanceof SVGElement -const resolveTarget = ( +export const resolveTarget = ( props: TeleportProps | null, select: RendererOptions['querySelector'] ): T | null => { diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index b182f7e7472..90f7b7efe03 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -86,7 +86,7 @@ export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode' // VNode types export { Fragment, Text, Comment, Static } from './vnode' // Built-in components -export { Teleport, TeleportProps } from './components/Teleport' +export { Teleport, TeleportProps, resolveTarget } from './components/Teleport' export { Suspense, SuspenseProps } from './components/Suspense' export { KeepAlive, KeepAliveProps } from './components/KeepAlive' export { diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index 93f972e99cb..296bd998d0d 100644 --- a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -216,4 +216,37 @@ describe('useCssVars', () => { expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red') } }) + + test('with teleport(change subTree)', async () => { + 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 a5ac6a0dda6..d1ecc6104e5 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -6,9 +6,12 @@ import { Static, watchPostEffect, onMounted, - onUnmounted + onUnmounted, + resolveTarget, + TeleportProps } from '@vue/runtime-core' -import { ShapeFlags } from '@vue/shared' +import { ShapeFlags, isArray } from '@vue/shared' +import { nodeOps } from '../nodeOps' /** * Runtime helper for SFC's CSS variable injection feature. @@ -28,11 +31,49 @@ export function useCssVars(getter: (ctx: any) => Record) { const setVars = () => setVarsOnVNode(instance.subTree, getter(instance.proxy!)) watchPostEffect(setVars) + onMounted(() => { - const ob = new MutationObserver(setVars) - ob.observe(instance.subTree.el!.parentNode, { childList: true }) - onUnmounted(() => ob.disconnect()) + const obs = [onSubTreeChange(instance.subTree.el!.parentNode, setVars)] + + const observeTeleportTarget = (vnode: VNode) => { + if (vnode.shapeFlag & ShapeFlags.TELEPORT) { + const target = resolveTarget( + vnode.props as TeleportProps, + nodeOps.querySelector + ) as Node + if (target) { + obs.push( + onSubTreeChange(target, (node: Node) => { + setVarsOnNode(node, getter(instance.proxy!)) + }) + ) + } + } + if (isArray(vnode.children)) { + vnode.children.forEach(n => observeTeleportTarget(n as VNode)) + } + } + observeTeleportTarget(instance.subTree) + + onUnmounted(() => { + obs.forEach(ob => ob.disconnect()) + }) + }) +} + +function onSubTreeChange( + target: Node, + cb: (...args: Node[]) => void +): MutationObserver { + const ob = new MutationObserver((mutations: MutationRecord[]) => { + mutations.forEach((mutation: MutationRecord) => { + mutation.addedNodes.forEach((node: Node) => { + cb(node) + }) + }) }) + ob.observe(target, { childList: true }) + return ob } function setVarsOnVNode(vnode: VNode, vars: Record) { From c38f995cc1c16e876c18893f7f6af4f5d9ab91f6 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 17 Sep 2021 21:17:31 +0800 Subject: [PATCH 4/4] chore: clean --- packages/runtime-dom/src/helpers/useCssVars.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index d1ecc6104e5..149c73afd5f 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -67,9 +67,7 @@ function onSubTreeChange( ): MutationObserver { const ob = new MutationObserver((mutations: MutationRecord[]) => { mutations.forEach((mutation: MutationRecord) => { - mutation.addedNodes.forEach((node: Node) => { - cb(node) - }) + mutation.addedNodes.forEach((node: Node) => cb(node)) }) }) ob.observe(target, { childList: true })