diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 31c910d17d4..ab2de223cdc 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -33,9 +33,9 @@ describe('hot module replacement', () => { }) test('createRecord', () => { - expect(createRecord('test1')).toBe(true) + expect(createRecord('test1', {})).toBe(true) // if id has already been created, should return false - expect(createRecord('test1')).toBe(false) + expect(createRecord('test1', {})).toBe(false) }) test('rerender', async () => { @@ -47,7 +47,7 @@ describe('hot module replacement', () => { __hmrId: childId, render: compileToFunction(`
`) } - createRecord(childId) + createRecord(childId, Child) const Parent: ComponentOptions = { __hmrId: parentId, @@ -59,7 +59,7 @@ describe('hot module replacement', () => { `
{{ count }}{{ count }}
` ) } - createRecord(parentId) + createRecord(parentId, Parent) render(h(Parent), root) expect(serializeInner(root)).toBe(`
0
0
`) @@ -125,7 +125,7 @@ describe('hot module replacement', () => { unmounted: unmountSpy, render: compileToFunction(`
{{ count }}
`) } - createRecord(childId) + createRecord(childId, Child) const Parent: ComponentOptions = { render: () => h(Child) @@ -164,7 +164,7 @@ describe('hot module replacement', () => { render: compileToFunction(`
{{ count }}
`) } } - createRecord(childId) + createRecord(childId, Child) const Parent: ComponentOptions = { render: () => h(Child) @@ -209,7 +209,7 @@ describe('hot module replacement', () => { }, render: compileToFunction(template) } - createRecord(id) + createRecord(id, Comp) render(h(Comp), root) expect(serializeInner(root)).toBe( @@ -246,14 +246,14 @@ describe('hot module replacement', () => { }, render: compileToFunction(`
{{ msg }}
`) } - createRecord(childId) + createRecord(childId, Child) const Parent: ComponentOptions = { __hmrId: parentId, components: { Child }, render: compileToFunction(``) } - createRecord(parentId) + createRecord(parentId, Parent) render(h(Parent), root) expect(serializeInner(root)).toBe(`
foo
`) @@ -272,14 +272,14 @@ describe('hot module replacement', () => { __hmrId: childId, render: compileToFunction(`
child
`) } - createRecord(childId) + createRecord(childId, Child) const Parent: ComponentOptions = { __hmrId: parentId, components: { Child }, render: compileToFunction(``) } - createRecord(parentId) + createRecord(parentId, Parent) render(h(Parent), root) expect(serializeInner(root)).toBe(`
child
`) @@ -299,7 +299,7 @@ describe('hot module replacement', () => { __hmrId: childId, render: compileToFunction(`
child
`) } - createRecord(childId) + createRecord(childId, Child) const components: ComponentOptions[] = [] @@ -321,7 +321,7 @@ describe('hot module replacement', () => { } } - createRecord(parentId) + createRecord(parentId, parentComp) } const last = components[components.length - 1] diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index faf4cb24e72..9a2d20def51 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -42,7 +42,10 @@ if (__DEV__ && (__BROWSER__ || __TEST__)) { } as HMRRuntime } -type HMRRecord = Set +type HMRRecord = { + component: ComponentOptions + instances: Set +} const map: Map = new Map() @@ -50,30 +53,37 @@ export function registerHMR(instance: ComponentInternalInstance) { const id = instance.type.__hmrId! let record = map.get(id) if (!record) { - createRecord(id) + createRecord(id, instance.type as ComponentOptions) record = map.get(id)! } - record.add(instance) + record.instances.add(instance) } export function unregisterHMR(instance: ComponentInternalInstance) { - map.get(instance.type.__hmrId!)!.delete(instance) + map.get(instance.type.__hmrId!)!.instances.delete(instance) } -function createRecord(id: string): boolean { +function createRecord( + id: string, + component: ComponentOptions | ClassComponent +): boolean { if (map.has(id)) { return false } - map.set(id, new Set()) + map.set(id, { + component: isClassComponent(component) ? component.__vccOpts : component, + instances: new Set() + }) return true } function rerender(id: string, newRender?: Function) { const record = map.get(id) if (!record) return + if (newRender) record.component.render = newRender // Array.from creates a snapshot which avoids the set being mutated during // updates - Array.from(record).forEach(instance => { + Array.from(record.instances).forEach(instance => { if (newRender) { instance.render = newRender as InternalRenderFunction } @@ -90,26 +100,27 @@ function reload(id: string, newComp: ComponentOptions | ClassComponent) { if (!record) return // Array.from creates a snapshot which avoids the set being mutated during // updates - Array.from(record).forEach(instance => { - const comp = instance.type - if (!hmrDirtyComponents.has(comp)) { - // 1. Update existing comp definition to match new one - newComp = isClassComponent(newComp) ? newComp.__vccOpts : newComp - extend(comp, newComp) - for (const key in comp) { - if (!(key in newComp)) { - delete (comp as any)[key] - } + const { component, instances } = record + + if (!hmrDirtyComponents.has(component)) { + // 1. Update existing comp definition to match new one + newComp = isClassComponent(newComp) ? newComp.__vccOpts : newComp + extend(component, newComp) + for (const key in component) { + if (!(key in newComp)) { + delete (component as any)[key] } - // 2. Mark component dirty. This forces the renderer to replace the component - // on patch. - hmrDirtyComponents.add(comp) - // 3. Make sure to unmark the component after the reload. - queuePostFlushCb(() => { - hmrDirtyComponents.delete(comp) - }) } + // 2. Mark component dirty. This forces the renderer to replace the component + // on patch. + hmrDirtyComponents.add(component) + // 3. Make sure to unmark the component after the reload. + queuePostFlushCb(() => { + hmrDirtyComponents.delete(component) + }) + } + Array.from(instances).forEach(instance => { 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