Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runtime): CSSVars can work with Teleport #7344

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b2d72c7
fix(runtime): cssVars can work with Teleport
baiwusanyu-c Dec 14, 2022
704be6f
fix(runtime): update code for unit test
baiwusanyu-c Dec 14, 2022
b8e882d
fix(runtime-core): add unit test
baiwusanyu-c Dec 14, 2022
673940c
fix(runtime-core): update code
baiwusanyu-c Dec 16, 2022
b34e64f
fix(runtime-core): update code
baiwusanyu-c Dec 16, 2022
f4a605c
fix(compiler-core): Add teleportIds in vnode to achieve correct rende…
baiwusanyu-c Dec 16, 2022
8020386
fix(runtime-core): Update judgment condition code
baiwusanyu-c Dec 17, 2022
adc9c58
fix(compiler-core): update unit test and support suspense
baiwusanyu-c Dec 17, 2022
80791fc
fix(compiler-core): added suspense's unit test
baiwusanyu-c Dec 17, 2022
ad95ea6
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Dec 26, 2022
1752fba
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Jan 2, 2023
fe147fc
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Jan 9, 2023
0dbec82
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Jan 14, 2023
be83253
Merge remote-tracking branch 'origin/main' into bwsy/fix/stringify
baiwusanyu-c Feb 4, 2023
3f2107e
Merge remote-tracking branch 'origin/main' into bwsy/fix/stringify
baiwusanyu-c Feb 4, 2023
0fa8a0a
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Feb 6, 2023
174aa23
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Feb 14, 2023
a6bed2e
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Feb 21, 2023
be7ef16
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Feb 22, 2023
13c9ab6
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Mar 17, 2023
0b7a33f
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Mar 20, 2023
df43c1e
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Mar 23, 2023
10d4368
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Mar 27, 2023
9bc3b32
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 6, 2023
da36b5c
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 10, 2023
5537755
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 12, 2023
50dbdcb
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 14, 2023
7fbc022
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 17, 2023
21c10f8
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 19, 2023
90361f8
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 20, 2023
2dd703d
Merge remote-tracking branch 'upstream/main' into bwsy/fix/teleportCS…
baiwusanyu-c Apr 21, 2023
b7cadc2
Merge branch 'vuejs:main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 25, 2023
fa64b07
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c May 4, 2023
1fc7b21
Merge remote-tracking branch 'origin/main' into bwsy/fix/teleportCSSVars
baiwusanyu-c May 9, 2023
d081bb4
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c May 16, 2023
dbec711
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Jun 6, 2023
69b2dcd
updated code
baiwusanyu-c Jul 12, 2023
6e0af13
Merge remote-tracking branch 'upstream/main' into bwsy/fix/teleportCS…
baiwusanyu-c Oct 20, 2023
97aae5f
Merge remote-tracking branch 'origin/main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Nov 13, 2023
e3e85d5
Merge remote-tracking branch 'origin/main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Dec 19, 2023
a12da2d
Merge remote-tracking branch 'origin/main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Jan 3, 2024
69a4c5f
[autofix.ci] apply automated fixes
autofix-ci[bot] Jan 3, 2024
7067594
chore: updated dts
baiwusanyu-c Jan 3, 2024
5e887ab
Merge remote-tracking branch 'upstream/main' into bwsy/fix/teleportCS…
baiwusanyu-c Mar 27, 2024
4ff76d4
Merge branch 'main' into bwsy/fix/teleportCSSVars
baiwusanyu-c Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/runtime-core/src/components/Teleport.ts
Expand Up @@ -28,7 +28,7 @@ const isTeleportDisabled = (props: VNode['props']): boolean =>
const isTargetSVG = (target: RendererElement): boolean =>
typeof SVGElement !== 'undefined' && target instanceof SVGElement

const resolveTarget = <T = RendererElement>(
export const resolveTarget = <T = RendererElement>(
props: TeleportProps | null,
select: RendererOptions['querySelector']
): T | null => {
Expand Down Expand Up @@ -396,13 +396,13 @@ export const Teleport = TeleportImpl as unknown as {
new (): { $props: VNodeProps & TeleportProps }
}

function updateCssVars(vnode: VNode) {
export function updateCssVars(vnode: VNode) {
// presence of .ut method indicates owner component uses css vars.
// code path here can assume browser environment.
const ctx = vnode.ctx
if (ctx && ctx.ut) {
let node = (vnode.children as VNode[])[0].el!
while (node !== vnode.targetAnchor) {
while (node && node !== vnode.targetAnchor) {
if (node.nodeType === 1) node.setAttribute('data-v-owner', ctx.uid)
node = node.nextSibling
}
Expand Down
7 changes: 6 additions & 1 deletion packages/runtime-core/src/index.ts
Expand Up @@ -88,7 +88,12 @@ export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode'
// VNode types
export { Fragment, Text, Comment, Static, VNodeRef } from './vnode'
// Built-in components
export { Teleport, TeleportProps } from './components/Teleport'
export {
Teleport,
TeleportProps,
resolveTarget,
updateCssVars
} from './components/Teleport'
export { Suspense, SuspenseProps } from './components/Suspense'
export { KeepAlive, KeepAliveProps } from './components/KeepAlive'
export {
Expand Down
117 changes: 116 additions & 1 deletion packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts
Expand Up @@ -9,7 +9,14 @@ import {
ComponentOptions,
Suspense,
Teleport,
FunctionalComponent
FunctionalComponent,
renderSlot,
withCtx,
openBlock,
createElementBlock,
createCommentVNode,
createVNode,
Transition
} from '@vue/runtime-dom'

describe('useCssVars', () => {
Expand Down Expand Up @@ -275,4 +282,112 @@ describe('useCssVars', () => {
expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe('red')
}
})

test('with teleport(component & v-if slot)', async () => {
document.body.innerHTML = ''
const state = reactive({ color: 'red' })
const root = document.createElement('div')
const target = document.body
const toggle = ref(true)
const comp = {
render(ctx: any) {
return renderSlot(ctx.$slots, 'default')
}
}
const App = {
setup() {
useCssVars(() => state)
return () => [
h(Teleport, { to: target }, [
h(
comp,
{},
{
default: withCtx(() => [
toggle.value
? (openBlock(),
createElementBlock(
'div',
{
key: 0,
class: 'text'
},
' test '
))
: createCommentVNode('v-if', true)
]),
_: 1
}
)
])
]
}
}

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 = false
await nextTick()
toggle.value = true
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')
}
})

test('with teleport(transition & v-if slot)', async () => {
document.body.innerHTML = ''
const state = reactive({ color: 'red' })
const root = document.createElement('div')
const target = document.body
const toggle = ref(true)

const App = {
setup() {
useCssVars(() => state)
return () => [
h(Teleport, { to: target }, [
createVNode(Transition, null, {
default: withCtx(() => [
toggle.value
? (openBlock(),
createElementBlock(
'div',
{
key: 0,
class: 'text'
},
'test'
))
: createCommentVNode('v-if', true)
]),
_: 1 /* STABLE */
})
])
]
}
}

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 = false
await nextTick()
toggle.value = true
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')
}
})
})
43 changes: 34 additions & 9 deletions packages/runtime-dom/src/helpers/useCssVars.ts
Expand Up @@ -6,10 +6,13 @@ import {
Static,
watchPostEffect,
onMounted,
onUnmounted
onUnmounted,
resolveTarget,
TeleportProps,
updateCssVars
} 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.
* @private
Expand All @@ -26,9 +29,11 @@ export function useCssVars(getter: (ctx: any) => Record<string, string>) {
}

const updateTeleports = (instance.ut = (vars = getter(instance.proxy)) => {
Array.from(
document.querySelectorAll(`[data-v-owner="${instance.uid}"]`)
).forEach(node => setVarsOnNode(node, vars))
if (document) {
Array.from(
document.querySelectorAll(`[data-v-owner="${instance.uid}"]`)
).forEach(node => setVarsOnNode(node, vars))
}
})

const setVars = () => {
Expand All @@ -40,12 +45,32 @@ export function useCssVars(getter: (ctx: any) => Record<string, string>) {
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, () => updateCssVars(vnode)))
}
}
if (isArray(vnode.children)) {
vnode.children.forEach(n => observeTeleportTarget(n as VNode))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

walk vnode tree and observe all teleport node,there may be a extra performance cost
Indeed,we need further consideration

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

walk vnode tree and observe all teleport node,there may be a extra performance cost Indeed,we need further consideration

I made some modifications, but as you said, I also think that doing so will consume a lot of performance.
I think a better approach should be like suspense , which handles the cssvars update on teleport at the right time during the runtime process, but that is a big change

}
}
observeTeleportTarget(instance.subTree)
onUnmounted(() => obs.forEach(ob => ob.disconnect()))
})
}

function onSubTreeChange(target: Node, cb: () => void): MutationObserver {
const ob = new MutationObserver(cb)
ob.observe(target, { childList: true })
return ob
}

function setVarsOnVNode(vnode: VNode, vars: Record<string, string>) {
if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
const suspense = vnode.suspense!
Expand Down