fix(hmr): make hmr working with class components (#2144)
This commit is contained in:
parent
57bdaa2220
commit
422f05e085
@ -148,6 +148,49 @@ describe('hot module replacement', () => {
|
|||||||
expect(mountSpy).toHaveBeenCalledTimes(1)
|
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
|
// #1156 - static nodes should retain DOM element reference across updates
|
||||||
// when HMR is active
|
// when HMR is active
|
||||||
test('static el reference', async () => {
|
test('static el reference', async () => {
|
||||||
|
@ -800,3 +800,7 @@ export function formatComponentName(
|
|||||||
|
|
||||||
return name ? classify(name) : isRoot ? `App` : `Anonymous`
|
return name ? classify(name) : isRoot ? `App` : `Anonymous`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isClassComponent(value: unknown): value is ClassComponent {
|
||||||
|
return isFunction(value) && '__vccOpts' in value
|
||||||
|
}
|
||||||
|
@ -3,7 +3,9 @@ import {
|
|||||||
ConcreteComponent,
|
ConcreteComponent,
|
||||||
ComponentInternalInstance,
|
ComponentInternalInstance,
|
||||||
ComponentOptions,
|
ComponentOptions,
|
||||||
InternalRenderFunction
|
InternalRenderFunction,
|
||||||
|
ClassComponent,
|
||||||
|
isClassComponent
|
||||||
} from './component'
|
} from './component'
|
||||||
import { queueJob, queuePostFlushCb } from './scheduler'
|
import { queueJob, queuePostFlushCb } from './scheduler'
|
||||||
import { extend } from '@vue/shared'
|
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)
|
const record = map.get(id)
|
||||||
if (!record) return
|
if (!record) return
|
||||||
// Array.from creates a snapshot which avoids the set being mutated during
|
// 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
|
const comp = instance.type
|
||||||
if (!hmrDirtyComponents.has(comp)) {
|
if (!hmrDirtyComponents.has(comp)) {
|
||||||
// 1. Update existing comp definition to match new one
|
// 1. Update existing comp definition to match new one
|
||||||
|
newComp = isClassComponent(newComp) ? newComp.__vccOpts : newComp
|
||||||
extend(comp, newComp)
|
extend(comp, newComp)
|
||||||
for (const key in comp) {
|
for (const key in comp) {
|
||||||
if (!(key in newComp)) {
|
if (!(key in newComp)) {
|
||||||
|
@ -17,7 +17,8 @@ import {
|
|||||||
Data,
|
Data,
|
||||||
ConcreteComponent,
|
ConcreteComponent,
|
||||||
ClassComponent,
|
ClassComponent,
|
||||||
Component
|
Component,
|
||||||
|
isClassComponent
|
||||||
} from './component'
|
} from './component'
|
||||||
import { RawSlots } from './componentSlots'
|
import { RawSlots } from './componentSlots'
|
||||||
import { isProxy, Ref, toRaw, ReactiveFlags } from '@vue/reactivity'
|
import { isProxy, Ref, toRaw, ReactiveFlags } from '@vue/reactivity'
|
||||||
@ -340,7 +341,7 @@ function _createVNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// class component normalization.
|
// class component normalization.
|
||||||
if (isFunction(type) && '__vccOpts' in type) {
|
if (isClassComponent(type)) {
|
||||||
type = type.__vccOpts
|
type = type.__vccOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user