From cfb5bef99bc721f71257addc2ba1d8be799353a5 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 13 Nov 2025 10:39:25 +0800 Subject: [PATCH] fix: ensure updateCssVars is called when subtree changed --- .../__tests__/helpers/useCssVars.spec.ts | 3 +- packages/runtime-vapor/src/apiCreateFor.ts | 1 + .../runtime-vapor/src/components/Teleport.ts | 32 +++++++++++++++---- packages/runtime-vapor/src/fragment.ts | 4 +++ packages/runtime-vapor/src/vdomInterop.ts | 3 ++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts index 0aac8ab479..1f73df6e0a 100644 --- a/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-vapor/__tests__/helpers/useCssVars.spec.ts @@ -270,8 +270,7 @@ describe('useVaporCssVars', () => { 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') - // TODO: problem is slot updateCssVars not called when slot changes - // expect((c as HTMLElement).outerHTML.includes('data-v-owner')).toBe(true) + expect((c as HTMLElement).outerHTML.includes('data-v-owner')).toBe(true) } }) diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index 5bc4473997..927809096f 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -402,6 +402,7 @@ export const createFor = ( } else { oldBlocks = [] } + if (frag.effects) frag.effects.forEach(effect => effect()) setActiveSub(prevSub) } diff --git a/packages/runtime-vapor/src/components/Teleport.ts b/packages/runtime-vapor/src/components/Teleport.ts index cb51406c4a..871da0a02c 100644 --- a/packages/runtime-vapor/src/components/Teleport.ts +++ b/packages/runtime-vapor/src/components/Teleport.ts @@ -21,7 +21,7 @@ import { import { rawPropsProxyHandlers } from '../componentProps' import { renderEffect } from '../renderEffect' import { extend, isArray } from '@vue/shared' -import { VaporFragment } from '../fragment' +import { VaporFragment, isFragment } from '../fragment' import { advanceHydrationNode, currentHydrationNode, @@ -48,6 +48,7 @@ export class TeleportFragment extends VaporFragment { private rawProps?: LooseRawProps private resolvedProps?: TeleportProps private rawSlots?: LooseRawSlots + isDisabled?: boolean target?: ParentNode | null targetAnchor?: Node | null @@ -78,6 +79,7 @@ export class TeleportFragment extends VaporFragment { rawPropsProxyHandlers, ) as any as TeleportProps, ) + this.isDisabled = isTeleportDisabled(this.resolvedProps!) this.handlePropsUpdate() }) @@ -97,8 +99,24 @@ export class TeleportFragment extends VaporFragment { ) }) + const nodes = this.nodes + // register updateCssVars to sub fragments's effects so that + // updateCssVars will be called when subtree changed + if (this.parentComponent && this.parentComponent.ut) { + if (isFragment(nodes)) { + ;(nodes.effects || (nodes.effects = [])).push(() => updateCssVars(this)) + } else if (isArray(nodes)) { + nodes.forEach(node => { + if (isFragment(node)) { + ;(node.effects || (node.effects = [])).push(() => + updateCssVars(this), + ) + } + }) + } + } + if (__DEV__) { - const nodes = this.nodes if (isVaporComponent(nodes)) { nodes.parentTeleport = this } else if (isArray(nodes)) { @@ -162,7 +180,7 @@ export class TeleportFragment extends VaporFragment { } mount(target, this.targetAnchor!) - updateCssVars(this, false) + updateCssVars(this) } else if (__DEV__) { warn( `Invalid Teleport target on ${this.targetAnchor ? 'update' : 'mount'}:`, @@ -173,9 +191,9 @@ export class TeleportFragment extends VaporFragment { } // mount into main container - if (isTeleportDisabled(this.resolvedProps!)) { + if (this.isDisabled) { mount(this.parent, this.anchor!) - updateCssVars(this, true) + updateCssVars(this) } // mount into target container else { @@ -333,11 +351,11 @@ function locateTeleportEndAnchor( return null } -function updateCssVars(frag: TeleportFragment, isDisabled: boolean) { +function updateCssVars(frag: TeleportFragment) { const ctx = frag.parentComponent as GenericComponentInstance if (ctx && ctx.ut) { let node, anchor - if (isDisabled) { + if (frag.isDisabled) { node = frag.placeholder anchor = frag.anchor } else { diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index e30909ea06..714a4b0fc8 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -56,6 +56,9 @@ export class VaporFragment refKey: string | undefined, ) => void + // effects to run after fragment render + effects?: (() => void)[] + constructor(nodes: T) { this.nodes = nodes } @@ -185,6 +188,7 @@ export class DynamicFragment extends VaporFragment { this.scope = undefined this.nodes = [] } + if (this.effects) this.effects.forEach(effect => effect()) } hydrate = (isEmpty = false): void => { diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index ab232ff8c7..86d754160b 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -380,6 +380,7 @@ function createVDOMComponent( } frag.nodes = vnode.el as any + if (frag.effects) frag.effects.forEach(effect => effect()) } frag.remove = unmount @@ -445,6 +446,8 @@ function renderVDOMSlot( internals.um(oldVNode, parentComponent as any, null) } } + + if (frag.effects) frag.effects.forEach(effect => effect()) } const render = (parentNode?: ParentNode, anchor?: Node | null) => { -- 2.47.3