Skip to content

Commit 4f4d13c

Browse files
committed
fix(hmr): track original __vapor state of components during HMR reload
1 parent 918b50f commit 4f4d13c

File tree

3 files changed

+39
-8
lines changed

3 files changed

+39
-8
lines changed

packages/runtime-core/src/components/BaseTransition.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
2121
import { PatchFlags, ShapeFlags, isArray, isFunction } from '@vue/shared'
2222
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
2323
import { isTeleport } from './Teleport'
24-
import { type RendererElement, getVaporInterface } from '../renderer'
24+
import {
25+
type RendererElement,
26+
getVaporInterface,
27+
isVaporComponent,
28+
} from '../renderer'
2529
import { SchedulerJobFlags } from '../scheduler'
2630

2731
type Hook<T = () => void> = T | T[]
@@ -140,7 +144,7 @@ export const BaseTransitionPropsValidators: Record<string, any> = {
140144
}
141145

142146
const recursiveGetSubtree = (instance: ComponentInternalInstance): VNode => {
143-
const subTree = instance.type.__vapor
147+
const subTree = isVaporComponent(instance.type as ConcreteComponent)
144148
? (instance as any).block
145149
: instance.subTree
146150
return subTree.component ? recursiveGetSubtree(subTree.component) : subTree
@@ -559,7 +563,7 @@ function getInnerChild(vnode: VNode): VNode | undefined {
559563

560564
export function setTransitionHooks(vnode: VNode, hooks: TransitionHooks): void {
561565
if (vnode.shapeFlag & ShapeFlags.COMPONENT && vnode.component) {
562-
if ((vnode.type as ConcreteComponent).__vapor) {
566+
if (isVaporComponent(vnode.type as ConcreteComponent)) {
563567
getVaporInterface(vnode.component, vnode).setTransitionHooks(
564568
vnode.component,
565569
hooks,

packages/runtime-core/src/hmr.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ export const hmrDirtyComponents: Map<
2020
Set<GenericComponentInstance>
2121
> = new Map<ConcreteComponent, Set<GenericComponentInstance>>()
2222

23+
// During HMR reload, `updateComponentDef` mutates the old component definition
24+
// with the new one's properties (including `__vapor`). This causes issues when
25+
// a component switches between vapor and vdom modes, because the renderer still
26+
// holds references to the old VNodes whose `type` now has an incorrect `__vapor`
27+
// value. This Map tracks the original `__vapor` state of dirty components so
28+
// that operations like unmount/move/getNextHostNode can use the correct mode.
29+
export const hmrDirtyComponentsMode: Map<ConcreteComponent, boolean> = new Map<
30+
ConcreteComponent,
31+
boolean
32+
>()
33+
2334
export interface HMRRuntime {
2435
createRecord: typeof createRecord
2536
rerender: typeof rerender
@@ -152,6 +163,7 @@ function reload(id: string, newComp: HMRComponent): void {
152163
hmrDirtyComponents.set(oldComp, (dirtyInstances = new Set()))
153164
}
154165
dirtyInstances.add(instance)
166+
hmrDirtyComponentsMode.set(oldComp, !!isVapor)
155167

156168
// 3. invalidate options resolution cache
157169
instance.appContext.propsCache.delete(instance.type as any)
@@ -206,6 +218,7 @@ function reload(id: string, newComp: HMRComponent): void {
206218
// 5. make sure to cleanup dirty hmr components after update
207219
queuePostFlushCb(() => {
208220
hmrDirtyComponents.clear()
221+
hmrDirtyComponentsMode.clear()
209222
})
210223
}
211224

packages/runtime-core/src/renderer.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,12 @@ import {
8383
type TeleportVNode,
8484
} from './components/Teleport'
8585
import { type KeepAliveContext, isKeepAlive } from './components/KeepAlive'
86-
import { isHmrUpdating, registerHMR, unregisterHMR } from './hmr'
86+
import {
87+
hmrDirtyComponentsMode,
88+
isHmrUpdating,
89+
registerHMR,
90+
unregisterHMR,
91+
} from './hmr'
8792
import { type RootHydrateFunction, createHydrationFunctions } from './hydration'
8893
import { invokeDirectiveHook } from './directives'
8994
import { endMeasure, startMeasure } from './profiling'
@@ -2177,7 +2182,7 @@ function baseCreateRenderer(
21772182
) => {
21782183
const { el, type, transition, children, shapeFlag } = vnode
21792184
if (shapeFlag & ShapeFlags.COMPONENT) {
2180-
if ((type as ConcreteComponent).__vapor) {
2185+
if (isVaporComponent(type as ConcreteComponent)) {
21812186
getVaporInterface(parentComponent, vnode).move(vnode, container, anchor)
21822187
} else {
21832188
move(
@@ -2309,7 +2314,7 @@ function baseCreateRenderer(
23092314
}
23102315

23112316
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
2312-
if ((vnode.type as ConcreteComponent).__vapor) {
2317+
if (isVaporComponent(vnode.type as ConcreteComponent)) {
23132318
getVaporInterface(parentComponent!, vnode).deactivate(
23142319
vnode,
23152320
(parentComponent!.ctx as KeepAliveContext).getStorageContainer(),
@@ -2332,7 +2337,7 @@ function baseCreateRenderer(
23322337
}
23332338

23342339
if (shapeFlag & ShapeFlags.COMPONENT) {
2335-
if ((type as ConcreteComponent).__vapor) {
2340+
if (isVaporComponent(type as ConcreteComponent)) {
23362341
getVaporInterface(parentComponent, vnode).unmount(vnode, doRemove)
23372342
return
23382343
} else {
@@ -2539,7 +2544,7 @@ function baseCreateRenderer(
25392544

25402545
const getNextHostNode: NextFn = vnode => {
25412546
if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
2542-
if ((vnode.type as ConcreteComponent).__vapor) {
2547+
if (isVaporComponent(vnode.type as ConcreteComponent)) {
25432548
return hostNextSibling(vnode.anchor!)
25442549
}
25452550
return getNextHostNode(vnode.component!.subTree)
@@ -2828,6 +2833,15 @@ export function getVaporInterface(
28282833
return res!
28292834
}
28302835

2836+
export function isVaporComponent(type: ConcreteComponent): boolean | undefined {
2837+
if (__DEV__ && isHmrUpdating) {
2838+
if (hmrDirtyComponentsMode.has(type)) {
2839+
return hmrDirtyComponentsMode.get(type)
2840+
}
2841+
}
2842+
return type.__vapor
2843+
}
2844+
28312845
/**
28322846
* shared between vdom and vapor
28332847
*/

0 commit comments

Comments
 (0)