From: daiwei Date: Wed, 26 Mar 2025 09:30:32 +0000 (+0800) Subject: wip: fix teleport root component hmr reload X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b474ce0a1e7d39d1884b23c44d684036a85fb3be;p=thirdparty%2Fvuejs%2Fcore.git wip: fix teleport root component hmr reload --- diff --git a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts index 20f5105472..d5ff414224 100644 --- a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts @@ -317,7 +317,7 @@ describe('renderer: VaporTeleport', () => { expect(target.innerHTML).toBe('') }) - test.todo('reload child + toggle disabled', async () => { + test('reload child + toggle disabled', async () => { const target = document.createElement('div') const root = document.createElement('div') const childId = 'test3-child' @@ -410,8 +410,6 @@ describe('renderer: VaporTeleport', () => { ) expect(target.innerHTML).toBe('') - //bug: child reload not update teleport fragment's nodes - // toggle disabled disabled.value = false await nextTick() diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 17ae719092..332595aac4 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -25,6 +25,7 @@ export class VaporFragment { anchor?: Node insert?: (parent: ParentNode, anchor: Node | null) => void remove?: (parent?: ParentNode) => void + getNodes?: () => Block constructor(nodes: Block) { this.nodes = nodes @@ -184,8 +185,8 @@ export function normalizeBlock(block: Block): Node[] { } else if (isVaporComponent(block)) { nodes.push(...normalizeBlock(block.block!)) } else { - if ((block as any).getNodes) { - nodes.push(...normalizeBlock((block as any).getNodes())) + if (block.getNodes) { + nodes.push(...normalizeBlock(block.getNodes())) } else { nodes.push(...normalizeBlock(block.nodes)) } diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 4d5c6d0b78..208b2d7787 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -60,7 +60,11 @@ import { import { hmrReload, hmrRerender } from './hmr' import { isHydrating, locateHydrationNode } from './dom/hydration' import { insertionAnchor, insertionParent } from './insertionState' -import type { VaporTeleportImpl } from './components/Teleport' +import { + type VaporTeleportImpl, + instanceToTeleportMap, + teleportStack, +} from './components/Teleport' export { currentInstance } from '@vue/runtime-dom' @@ -209,6 +213,11 @@ export function createComponent( ) if (__DEV__) { + let teleport = teleportStack[teleportStack.length - 1] + if (teleport) { + instanceToTeleportMap.set(instance, teleport) + } + pushWarningContext(instance) startMeasure(instance, `init`) @@ -296,7 +305,7 @@ export function createComponent( onScopeDispose(() => unmountComponent(instance), true) if (!isHydrating && _insertionParent) { - insert(instance.block, _insertionParent, _insertionAnchor) + mountComponent(instance, _insertionParent, _insertionAnchor) } return instance diff --git a/packages/runtime-vapor/src/components/Teleport.ts b/packages/runtime-vapor/src/components/Teleport.ts index 6c0b089bf0..ab900951da 100644 --- a/packages/runtime-vapor/src/components/Teleport.ts +++ b/packages/runtime-vapor/src/components/Teleport.ts @@ -15,12 +15,45 @@ import { remove, } from '../block' import { createComment, createTextNode, querySelector } from '../dom/node' -import type { LooseRawProps, LooseRawSlots } from '../component' +import type { + LooseRawProps, + LooseRawSlots, + VaporComponentInstance, +} from '../component' import { rawPropsProxyHandlers } from '../componentProps' import { renderEffect } from '../renderEffect' -import { extend } from '@vue/shared' +import { extend, isArray } from '@vue/shared' import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity' +export const teleportStack: TeleportFragment[] = [] +export const instanceToTeleportMap: WeakMap< + VaporComponentInstance, + TeleportFragment +> = __DEV__ ? new WeakMap() : (null as any) + +/** + * dev only. + * when the **root** child component updates, synchronously update + * the TeleportFragment's children and nodes. + */ +export function handleTeleportChildrenHmrReload( + instance: VaporComponentInstance, + newInstance: VaporComponentInstance, +): void { + const teleport = instanceToTeleportMap.get(instance) + if (teleport) { + instanceToTeleportMap.set(newInstance, teleport) + if (teleport.nodes === instance) { + teleport.children = teleport.nodes = newInstance + } else if (isArray(teleport.nodes)) { + const i = teleport.nodes.indexOf(instance) + if (i > -1) { + ;(teleport.children as Block[])[i] = teleport.nodes[i] = newInstance + } + } + } +} + export const VaporTeleportImpl = { name: 'VaporTeleport', __isTeleport: true, @@ -34,11 +67,12 @@ export const VaporTeleportImpl = { pauseTracking() const scope = (frag.scope = new EffectScope()) scope!.run(() => { - let children: Block renderEffect(() => { + teleportStack.push(frag) frag.updateChildren( - (children = slots.default && (slots.default as BlockFn)()), + (frag.children = slots.default && (slots.default as BlockFn)()), ) + teleportStack.pop() }) renderEffect(() => { @@ -48,15 +82,17 @@ export const VaporTeleportImpl = { {}, new Proxy(props, rawPropsProxyHandlers) as any as TeleportProps, ), - children!, + frag.children!, ) }) }) resetTracking() if (__DEV__) { - // TODO - ;(frag as any).getNodes = () => { + // used in normalizeBlock to get the nodes of a TeleportFragment + // during hmr update. return empty array if the teleport content + // is mounted into the target container. + frag.getNodes = () => { return frag.parent !== frag.currentParent ? [] : frag.nodes } } @@ -68,6 +104,7 @@ export const VaporTeleportImpl = { class TeleportFragment extends VaporFragment { anchor: Node scope: EffectScope | undefined + children: Block | undefined private targetStart?: Node private mainAnchor?: Node @@ -178,7 +215,7 @@ class TeleportFragment extends VaporFragment { // remove nodes if (this.nodes) { remove(this.nodes, this.currentParent) - this.nodes = [] + this.children = this.nodes = [] } // remove anchors diff --git a/packages/runtime-vapor/src/hmr.ts b/packages/runtime-vapor/src/hmr.ts index 5df5d2a469..ba66936049 100644 --- a/packages/runtime-vapor/src/hmr.ts +++ b/packages/runtime-vapor/src/hmr.ts @@ -13,6 +13,7 @@ import { mountComponent, unmountComponent, } from './component' +import { handleTeleportChildrenHmrReload } from './components/Teleport' export function hmrRerender(instance: VaporComponentInstance): void { const normalized = normalizeBlock(instance.block) @@ -54,5 +55,5 @@ export function hmrReload( ) simpleSetCurrentInstance(prev, instance.parent) mountComponent(newInstance, parent, anchor) - instance.block = newInstance.block + handleTeleportChildrenHmrReload(instance, newInstance) }