From e8067f1cd987e39b218c87b1c594510bbe7a8ee4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 8 Dec 2024 16:32:29 +0800 Subject: [PATCH] wip: prepare hmr for vapor --- packages/runtime-core/src/component.ts | 4 + packages/runtime-core/src/hmr.ts | 125 +++++++++++++------------ 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c7424abc7..f9dae3d21 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -348,6 +348,10 @@ export interface GenericComponentInstance { * @internal */ scope: EffectScope + /** + * render function will have different types between vdom and vapor + */ + render?: Function | null /** * SSR render function * (they are the same between vdom and vapor components.) diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index 7aedf52dd..643f8ffff 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -4,7 +4,7 @@ import { type ComponentInternalInstance, type ComponentOptions, type ConcreteComponent, - type InternalRenderFunction, + type GenericComponentInstance, isClassComponent, } from './component' import { queueJob, queuePostFlushCb } from './scheduler' @@ -16,8 +16,8 @@ export let isHmrUpdating = false export const hmrDirtyComponents: Map< ConcreteComponent, - Set -> = new Map>() + Set +> = new Map>() export interface HMRRuntime { createRecord: typeof createRecord @@ -45,11 +45,11 @@ const map: Map< // to apply hot updates to the component even when there are no actively // rendered instance. initialDef: ComponentOptions - instances: Set + instances: Set } > = new Map() -export function registerHMR(instance: ComponentInternalInstance): void { +export function registerHMR(instance: GenericComponentInstance): void { const id = instance.type.__hmrId! let record = map.get(id) if (!record) { @@ -59,7 +59,7 @@ export function registerHMR(instance: ComponentInternalInstance): void { record.instances.add(instance) } -export function unregisterHMR(instance: ComponentInternalInstance): void { +export function unregisterHMR(instance: GenericComponentInstance): void { map.get(instance.type.__hmrId!)!.instances.delete(instance) } @@ -90,13 +90,18 @@ function rerender(id: string, newRender?: Function): void { // Create a snapshot which avoids the set being mutated during updates ;[...record.instances].forEach(instance => { if (newRender) { - instance.render = newRender as InternalRenderFunction + instance.render = newRender normalizeClassComponent(instance.type as HMRComponent).render = newRender } - instance.renderCache = [] // this flag forces child components with slot content to update isHmrUpdating = true - instance.update() + if (instance.vapor) { + // TODO + } else { + const i = instance as ComponentInternalInstance + i.renderCache = [] + i.update() + } isHmrUpdating = false }) } @@ -112,62 +117,64 @@ function reload(id: string, newComp: HMRComponent): void { // create a snapshot which avoids the set being mutated during updates const instances = [...record.instances] - for (let i = 0; i < instances.length; i++) { - const instance = instances[i] - const oldComp = normalizeClassComponent(instance.type as HMRComponent) + if (newComp.vapor) { + // TODO + } else { + for (const instance of instances as ComponentInternalInstance[]) { + const oldComp = normalizeClassComponent(instance.type as HMRComponent) - let dirtyInstances = hmrDirtyComponents.get(oldComp) - if (!dirtyInstances) { - // 1. Update existing comp definition to match new one - if (oldComp !== record.initialDef) { - updateComponentDef(oldComp, newComp) + let dirtyInstances = hmrDirtyComponents.get(oldComp) + if (!dirtyInstances) { + // 1. Update existing comp definition to match new one + if (oldComp !== record.initialDef) { + updateComponentDef(oldComp, newComp) + } + // 2. mark definition dirty. This forces the renderer to replace the + // component on patch. + hmrDirtyComponents.set(oldComp, (dirtyInstances = new Set())) } - // 2. mark definition dirty. This forces the renderer to replace the - // component on patch. - hmrDirtyComponents.set(oldComp, (dirtyInstances = new Set())) - } - dirtyInstances.add(instance) - - // 3. invalidate options resolution cache - instance.appContext.propsCache.delete(instance.type as any) - instance.appContext.emitsCache.delete(instance.type as any) - instance.appContext.optionsCache.delete(instance.type as any) - - // 4. actually update - if (instance.ceReload) { - // custom element dirtyInstances.add(instance) - instance.ceReload((newComp as any).styles) - dirtyInstances.delete(instance) - } else if (instance.parent) { - // 4. Force the parent instance to re-render. This will cause all updated - // components to be unmounted and re-mounted. Queue the update so that we - // don't end up forcing the same parent to re-render multiple times. - queueJob(() => { - isHmrUpdating = true - instance.parent!.update() - isHmrUpdating = false - // #6930, #11248 avoid infinite recursion - dirtyInstances.delete(instance) - }) - } else if (instance.appContext.reload) { - // root instance mounted via createApp() has a reload method - instance.appContext.reload() - } else if (typeof window !== 'undefined') { - // root instance inside tree created via raw render(). Force reload. - window.location.reload() - } else { - console.warn( - '[HMR] Root or manually mounted instance modified. Full reload required.', - ) - } - // update custom element child style - if (instance.root.ce && instance !== instance.root) { - instance.root.ce._removeChildStyle(oldComp) + // 3. invalidate options resolution cache + instance.appContext.propsCache.delete(instance.type as any) + instance.appContext.emitsCache.delete(instance.type as any) + instance.appContext.optionsCache.delete(instance.type as any) + + // 4. actually update + if (instance.ceReload) { + // custom element + dirtyInstances.add(instance) + instance.ceReload((newComp as any).styles) + dirtyInstances.delete(instance) + } else if (instance.parent) { + // 4. Force the parent instance to re-render. This will cause all updated + // components to be unmounted and re-mounted. Queue the update so that we + // don't end up forcing the same parent to re-render multiple times. + queueJob(() => { + isHmrUpdating = true + instance.parent!.update() + isHmrUpdating = false + // #6930, #11248 avoid infinite recursion + dirtyInstances.delete(instance) + }) + } else if (instance.appContext.reload) { + // root instance mounted via createApp() has a reload method + instance.appContext.reload() + } else if (typeof window !== 'undefined') { + // root instance inside tree created via raw render(). Force reload. + window.location.reload() + } else { + console.warn( + '[HMR] Root or manually mounted instance modified. Full reload required.', + ) + } + + // update custom element child style + if (instance.root.ce && instance !== instance.root) { + instance.root.ce._removeChildStyle(oldComp) + } } } - // 5. make sure to cleanup dirty hmr components after update queuePostFlushCb(() => { hmrDirtyComponents.clear()