From fc983516f3f16ff54468bb3ba7941f80b5312fc7 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 9 Dec 2025 15:12:47 +0800 Subject: [PATCH 1/2] fix(hmr): handle reload for template-only components switching between vapor and vdom --- packages/runtime-core/src/hmr.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index 1d35bd34692..2c5cc54fa64 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -117,13 +117,14 @@ function reload(id: string, newComp: HMRComponent): void { if (!record) return newComp = normalizeClassComponent(newComp) + const isVapor = record.initialDef.__vapor // update initial def (for not-yet-rendered components) updateComponentDef(record.initialDef, newComp) // create a snapshot which avoids the set being mutated during updates const instances = [...record.instances] - if (newComp.__vapor && !instances.some(i => i.ceReload)) { + if (isVapor && newComp.__vapor && !instances.some(i => i.ceReload)) { // For multiple instances with the same __hmrId, remove styles first before reload // to avoid the second instance's style removal deleting the first instance's // newly added styles (since hmrReload is synchronous) From 038e5a987003749ee21c57ca24d1b59018b27545 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 9 Dec 2025 16:49:40 +0800 Subject: [PATCH 2/2] fix(hmr): refactor scope cleanup to use reset method for stale effects management --- packages/reactivity/src/effectScope.ts | 15 +++++++++++---- packages/runtime-vapor/src/component.ts | 3 +-- packages/runtime-vapor/src/hmr.ts | 6 ++---- packages/runtime-vapor/src/renderEffect.ts | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 36c9b85e8d7..105ca5a9d6a 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -73,6 +73,17 @@ export class EffectScope implements ReactiveNode { return } this.flags = EffectFlags.STOP + this.reset() + const sub = this.subs + if (sub !== undefined) { + unlink(sub) + } + } + + /** + * @internal + */ + reset(): void { let dep = this.deps while (dep !== undefined) { const node = dep.dep @@ -83,10 +94,6 @@ export class EffectScope implements ReactiveNode { dep = unlink(dep, this) } } - const sub = this.subs - if (sub !== undefined) { - unlink(sub) - } cleanup(this) } } diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index c66e5f90d1c..cd920471b2c 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -592,8 +592,7 @@ export class VaporComponentInstance< asyncDep: Promise | null asyncResolved: boolean - // for HMR and vapor custom element - // all render effects associated with this instance + // for vapor custom element renderEffects?: RenderEffect[] hasFallthrough: boolean diff --git a/packages/runtime-vapor/src/hmr.ts b/packages/runtime-vapor/src/hmr.ts index f045595b548..521cc6ae6c8 100644 --- a/packages/runtime-vapor/src/hmr.ts +++ b/packages/runtime-vapor/src/hmr.ts @@ -19,12 +19,10 @@ export function hmrRerender(instance: VaporComponentInstance): void { const normalized = normalizeBlock(instance.block) const parent = normalized[0].parentNode! const anchor = normalized[normalized.length - 1].nextSibling + // reset scope to avoid stale effects + instance.scope.reset() remove(instance.block, parent) const prev = setCurrentInstance(instance) - if (instance.renderEffects) { - instance.renderEffects.forEach(e => e.stop()) - instance.renderEffects = [] - } pushWarningContext(instance) devRender(instance) popWarningContext() diff --git a/packages/runtime-vapor/src/renderEffect.ts b/packages/runtime-vapor/src/renderEffect.ts index ce8eaf46afd..08f12614210 100644 --- a/packages/runtime-vapor/src/renderEffect.ts +++ b/packages/runtime-vapor/src/renderEffect.ts @@ -44,8 +44,8 @@ export class RenderEffect extends ReactiveEffect { : void 0 } - if (__DEV__ || instance.type.ce) { - // register effect for stopping them during HMR rerender + // register effect for vapor custom element update + if (instance.type.ce) { ;(instance.renderEffects || (instance.renderEffects = [])).push(this) } job.i = instance