From 422f05e085036e23ea3632c2ce75d86181a087b8 Mon Sep 17 00:00:00 2001 From: Katashin Date: Fri, 18 Sep 2020 12:14:59 +0800 Subject: [PATCH] fix(hmr): make hmr working with class components (#2144) --- packages/runtime-core/__tests__/hmr.spec.ts | 43 +++++++++++++++++++++ packages/runtime-core/src/component.ts | 4 ++ packages/runtime-core/src/hmr.ts | 7 +++- packages/runtime-core/src/vnode.ts | 5 ++- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index a53c7a3e..31c910d1 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -148,6 +148,49 @@ describe('hot module replacement', () => { expect(mountSpy).toHaveBeenCalledTimes(1) }) + test('reload class component', async () => { + const root = nodeOps.createElement('div') + const childId = 'test4-child' + const unmountSpy = jest.fn() + const mountSpy = jest.fn() + + class Child { + static __vccOpts: ComponentOptions = { + __hmrId: childId, + data() { + return { count: 0 } + }, + unmounted: unmountSpy, + render: compileToFunction(`
{{ count }}
`) + } + } + createRecord(childId) + + const Parent: ComponentOptions = { + render: () => h(Child) + } + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
0
`) + + class UpdatedChild { + static __vccOpts: ComponentOptions = { + __hmrId: childId, + data() { + return { count: 1 } + }, + mounted: mountSpy, + render: compileToFunction(`
{{ count }}
`) + } + } + + reload(childId, UpdatedChild) + await nextTick() + expect(serializeInner(root)).toBe(`
1
`) + expect(unmountSpy).toHaveBeenCalledTimes(1) + expect(mountSpy).toHaveBeenCalledTimes(1) + }) + // #1156 - static nodes should retain DOM element reference across updates // when HMR is active test('static el reference', async () => { diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index ac482d67..78abad60 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -800,3 +800,7 @@ export function formatComponentName( return name ? classify(name) : isRoot ? `App` : `Anonymous` } + +export function isClassComponent(value: unknown): value is ClassComponent { + return isFunction(value) && '__vccOpts' in value +} diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index b831498b..df557313 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -3,7 +3,9 @@ import { ConcreteComponent, ComponentInternalInstance, ComponentOptions, - InternalRenderFunction + InternalRenderFunction, + ClassComponent, + isClassComponent } from './component' import { queueJob, queuePostFlushCb } from './scheduler' import { extend } from '@vue/shared' @@ -83,7 +85,7 @@ function rerender(id: string, newRender?: Function) { }) } -function reload(id: string, newComp: ComponentOptions) { +function reload(id: string, newComp: ComponentOptions | ClassComponent) { const record = map.get(id) if (!record) return // Array.from creates a snapshot which avoids the set being mutated during @@ -92,6 +94,7 @@ function reload(id: string, newComp: ComponentOptions) { 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)) { diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 9a11090a..56ff85f5 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -17,7 +17,8 @@ import { Data, ConcreteComponent, ClassComponent, - Component + Component, + isClassComponent } from './component' import { RawSlots } from './componentSlots' import { isProxy, Ref, toRaw, ReactiveFlags } from '@vue/reactivity' @@ -340,7 +341,7 @@ function _createVNode( } // class component normalization. - if (isFunction(type) && '__vccOpts' in type) { + if (isClassComponent(type)) { type = type.__vccOpts }