Skip to content

Commit b474ce0

Browse files
committed
wip: fix teleport root component hmr reload
1 parent b646856 commit b474ce0

File tree

5 files changed

+62
-16
lines changed

5 files changed

+62
-16
lines changed

packages/runtime-vapor/__tests__/components/Teleport.spec.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ describe('renderer: VaporTeleport', () => {
317317
expect(target.innerHTML).toBe('')
318318
})
319319

320-
test.todo('reload child + toggle disabled', async () => {
320+
test('reload child + toggle disabled', async () => {
321321
const target = document.createElement('div')
322322
const root = document.createElement('div')
323323
const childId = 'test3-child'
@@ -410,8 +410,6 @@ describe('renderer: VaporTeleport', () => {
410410
)
411411
expect(target.innerHTML).toBe('')
412412

413-
//bug: child reload not update teleport fragment's nodes
414-
415413
// toggle disabled
416414
disabled.value = false
417415
await nextTick()

packages/runtime-vapor/src/block.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export class VaporFragment {
2525
anchor?: Node
2626
insert?: (parent: ParentNode, anchor: Node | null) => void
2727
remove?: (parent?: ParentNode) => void
28+
getNodes?: () => Block
2829

2930
constructor(nodes: Block) {
3031
this.nodes = nodes
@@ -184,8 +185,8 @@ export function normalizeBlock(block: Block): Node[] {
184185
} else if (isVaporComponent(block)) {
185186
nodes.push(...normalizeBlock(block.block!))
186187
} else {
187-
if ((block as any).getNodes) {
188-
nodes.push(...normalizeBlock((block as any).getNodes()))
188+
if (block.getNodes) {
189+
nodes.push(...normalizeBlock(block.getNodes()))
189190
} else {
190191
nodes.push(...normalizeBlock(block.nodes))
191192
}

packages/runtime-vapor/src/component.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ import {
6060
import { hmrReload, hmrRerender } from './hmr'
6161
import { isHydrating, locateHydrationNode } from './dom/hydration'
6262
import { insertionAnchor, insertionParent } from './insertionState'
63-
import type { VaporTeleportImpl } from './components/Teleport'
63+
import {
64+
type VaporTeleportImpl,
65+
instanceToTeleportMap,
66+
teleportStack,
67+
} from './components/Teleport'
6468

6569
export { currentInstance } from '@vue/runtime-dom'
6670

@@ -209,6 +213,11 @@ export function createComponent(
209213
)
210214

211215
if (__DEV__) {
216+
let teleport = teleportStack[teleportStack.length - 1]
217+
if (teleport) {
218+
instanceToTeleportMap.set(instance, teleport)
219+
}
220+
212221
pushWarningContext(instance)
213222
startMeasure(instance, `init`)
214223

@@ -296,7 +305,7 @@ export function createComponent(
296305
onScopeDispose(() => unmountComponent(instance), true)
297306

298307
if (!isHydrating && _insertionParent) {
299-
insert(instance.block, _insertionParent, _insertionAnchor)
308+
mountComponent(instance, _insertionParent, _insertionAnchor)
300309
}
301310

302311
return instance

packages/runtime-vapor/src/components/Teleport.ts

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,45 @@ import {
1515
remove,
1616
} from '../block'
1717
import { createComment, createTextNode, querySelector } from '../dom/node'
18-
import type { LooseRawProps, LooseRawSlots } from '../component'
18+
import type {
19+
LooseRawProps,
20+
LooseRawSlots,
21+
VaporComponentInstance,
22+
} from '../component'
1923
import { rawPropsProxyHandlers } from '../componentProps'
2024
import { renderEffect } from '../renderEffect'
21-
import { extend } from '@vue/shared'
25+
import { extend, isArray } from '@vue/shared'
2226
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
2327

28+
export const teleportStack: TeleportFragment[] = []
29+
export const instanceToTeleportMap: WeakMap<
30+
VaporComponentInstance,
31+
TeleportFragment
32+
> = __DEV__ ? new WeakMap() : (null as any)
33+
34+
/**
35+
* dev only.
36+
* when the **root** child component updates, synchronously update
37+
* the TeleportFragment's children and nodes.
38+
*/
39+
export function handleTeleportChildrenHmrReload(
40+
instance: VaporComponentInstance,
41+
newInstance: VaporComponentInstance,
42+
): void {
43+
const teleport = instanceToTeleportMap.get(instance)
44+
if (teleport) {
45+
instanceToTeleportMap.set(newInstance, teleport)
46+
if (teleport.nodes === instance) {
47+
teleport.children = teleport.nodes = newInstance
48+
} else if (isArray(teleport.nodes)) {
49+
const i = teleport.nodes.indexOf(instance)
50+
if (i > -1) {
51+
;(teleport.children as Block[])[i] = teleport.nodes[i] = newInstance
52+
}
53+
}
54+
}
55+
}
56+
2457
export const VaporTeleportImpl = {
2558
name: 'VaporTeleport',
2659
__isTeleport: true,
@@ -34,11 +67,12 @@ export const VaporTeleportImpl = {
3467
pauseTracking()
3568
const scope = (frag.scope = new EffectScope())
3669
scope!.run(() => {
37-
let children: Block
3870
renderEffect(() => {
71+
teleportStack.push(frag)
3972
frag.updateChildren(
40-
(children = slots.default && (slots.default as BlockFn)()),
73+
(frag.children = slots.default && (slots.default as BlockFn)()),
4174
)
75+
teleportStack.pop()
4276
})
4377

4478
renderEffect(() => {
@@ -48,15 +82,17 @@ export const VaporTeleportImpl = {
4882
{},
4983
new Proxy(props, rawPropsProxyHandlers) as any as TeleportProps,
5084
),
51-
children!,
85+
frag.children!,
5286
)
5387
})
5488
})
5589
resetTracking()
5690

5791
if (__DEV__) {
58-
// TODO
59-
;(frag as any).getNodes = () => {
92+
// used in normalizeBlock to get the nodes of a TeleportFragment
93+
// during hmr update. return empty array if the teleport content
94+
// is mounted into the target container.
95+
frag.getNodes = () => {
6096
return frag.parent !== frag.currentParent ? [] : frag.nodes
6197
}
6298
}
@@ -68,6 +104,7 @@ export const VaporTeleportImpl = {
68104
class TeleportFragment extends VaporFragment {
69105
anchor: Node
70106
scope: EffectScope | undefined
107+
children: Block | undefined
71108

72109
private targetStart?: Node
73110
private mainAnchor?: Node
@@ -178,7 +215,7 @@ class TeleportFragment extends VaporFragment {
178215
// remove nodes
179216
if (this.nodes) {
180217
remove(this.nodes, this.currentParent)
181-
this.nodes = []
218+
this.children = this.nodes = []
182219
}
183220

184221
// remove anchors

packages/runtime-vapor/src/hmr.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
mountComponent,
1414
unmountComponent,
1515
} from './component'
16+
import { handleTeleportChildrenHmrReload } from './components/Teleport'
1617

1718
export function hmrRerender(instance: VaporComponentInstance): void {
1819
const normalized = normalizeBlock(instance.block)
@@ -54,5 +55,5 @@ export function hmrReload(
5455
)
5556
simpleSetCurrentInstance(prev, instance.parent)
5657
mountComponent(newInstance, parent, anchor)
57-
instance.block = newInstance.block
58+
handleTeleportChildrenHmrReload(instance, newInstance)
5859
}

0 commit comments

Comments
 (0)