Skip to content

Commit

Permalink
fix(hmr): make hmr working with class components (#2144)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktsn authored Sep 18, 2020
1 parent 57bdaa2 commit 422f05e
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
43 changes: 43 additions & 0 deletions packages/runtime-core/__tests__/hmr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(`<div @click="count++">{{ count }}</div>`)
}
}
createRecord(childId)

const Parent: ComponentOptions = {
render: () => h(Child)
}

render(h(Parent), root)
expect(serializeInner(root)).toBe(`<div>0</div>`)

class UpdatedChild {
static __vccOpts: ComponentOptions = {
__hmrId: childId,
data() {
return { count: 1 }
},
mounted: mountSpy,
render: compileToFunction(`<div @click="count++">{{ count }}</div>`)
}
}

reload(childId, UpdatedChild)
await nextTick()
expect(serializeInner(root)).toBe(`<div>1</div>`)
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 () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
7 changes: 5 additions & 2 deletions packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {
ConcreteComponent,
ComponentInternalInstance,
ComponentOptions,
InternalRenderFunction
InternalRenderFunction,
ClassComponent,
isClassComponent
} from './component'
import { queueJob, queuePostFlushCb } from './scheduler'
import { extend } from '@vue/shared'
Expand Down Expand Up @@ -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
Expand All @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions packages/runtime-core/src/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -340,7 +341,7 @@ function _createVNode(
}

// class component normalization.
if (isFunction(type) && '__vccOpts' in type) {
if (isClassComponent(type)) {
type = type.__vccOpts
}

Expand Down

0 comments on commit 422f05e

Please sign in to comment.