fix(hmr): handle cases where instances with same id having different definitions
This commit is contained in:
parent
0c48558f4c
commit
01b7e90eac
@ -33,10 +33,7 @@ if (__DEV__) {
|
|||||||
} as HMRRuntime
|
} as HMRRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HMRRecord {
|
type HMRRecord = Set<ComponentInternalInstance>
|
||||||
comp: ComponentOptions
|
|
||||||
instances: Set<ComponentInternalInstance>
|
|
||||||
}
|
|
||||||
|
|
||||||
const map: Map<string, HMRRecord> = new Map()
|
const map: Map<string, HMRRecord> = new Map()
|
||||||
|
|
||||||
@ -47,21 +44,18 @@ export function registerHMR(instance: ComponentInternalInstance) {
|
|||||||
createRecord(id, instance.type as ComponentOptions)
|
createRecord(id, instance.type as ComponentOptions)
|
||||||
record = map.get(id)!
|
record = map.get(id)!
|
||||||
}
|
}
|
||||||
record.instances.add(instance)
|
record.add(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unregisterHMR(instance: ComponentInternalInstance) {
|
export function unregisterHMR(instance: ComponentInternalInstance) {
|
||||||
map.get(instance.type.__hmrId!)!.instances.delete(instance)
|
map.get(instance.type.__hmrId!)!.delete(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRecord(id: string, comp: ComponentOptions): boolean {
|
function createRecord(id: string, comp: ComponentOptions): boolean {
|
||||||
if (map.has(id)) {
|
if (map.has(id)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
map.set(id, {
|
map.set(id, new Set())
|
||||||
comp,
|
|
||||||
instances: new Set()
|
|
||||||
})
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +64,7 @@ function rerender(id: string, newRender?: Function) {
|
|||||||
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
|
||||||
// updates
|
// updates
|
||||||
Array.from(record.instances).forEach(instance => {
|
Array.from(record).forEach(instance => {
|
||||||
if (newRender) {
|
if (newRender) {
|
||||||
instance.render = newRender as InternalRenderFunction
|
instance.render = newRender as InternalRenderFunction
|
||||||
}
|
}
|
||||||
@ -85,22 +79,29 @@ function rerender(id: string, newRender?: Function) {
|
|||||||
function reload(id: string, newComp: ComponentOptions) {
|
function reload(id: string, newComp: ComponentOptions) {
|
||||||
const record = map.get(id)
|
const record = map.get(id)
|
||||||
if (!record) return
|
if (!record) return
|
||||||
// 1. Update existing comp definition to match new one
|
|
||||||
const comp = record.comp
|
|
||||||
Object.assign(comp, newComp)
|
|
||||||
for (const key in comp) {
|
|
||||||
if (!(key in newComp)) {
|
|
||||||
delete (comp as any)[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 2. Mark component dirty. This forces the renderer to replace the component
|
|
||||||
// on patch.
|
|
||||||
comp.__hmrUpdated = true
|
|
||||||
// Array.from creates a snapshot which avoids the set being mutated during
|
// Array.from creates a snapshot which avoids the set being mutated during
|
||||||
// updates
|
// updates
|
||||||
Array.from(record.instances).forEach(instance => {
|
Array.from(record).forEach(instance => {
|
||||||
|
const comp = instance.type
|
||||||
|
if (!comp.__hmrUpdated) {
|
||||||
|
// 1. Update existing comp definition to match new one
|
||||||
|
Object.assign(comp, newComp)
|
||||||
|
for (const key in comp) {
|
||||||
|
if (!(key in newComp)) {
|
||||||
|
delete (comp as any)[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. Mark component dirty. This forces the renderer to replace the component
|
||||||
|
// on patch.
|
||||||
|
comp.__hmrUpdated = true
|
||||||
|
// 3. Make sure to unmark the component after the reload.
|
||||||
|
queuePostFlushCb(() => {
|
||||||
|
comp.__hmrUpdated = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (instance.parent) {
|
if (instance.parent) {
|
||||||
// 3. Force the parent instance to re-render. This will cause all updated
|
// 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
|
// 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.
|
// don't end up forcing the same parent to re-render multiple times.
|
||||||
queueJob(instance.parent.update)
|
queueJob(instance.parent.update)
|
||||||
@ -116,10 +117,6 @@ function reload(id: string, newComp: ComponentOptions) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 4. Make sure to unmark the component after the reload.
|
|
||||||
queuePostFlushCb(() => {
|
|
||||||
comp.__hmrUpdated = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryWrap(fn: (id: string, arg: any) => any): Function {
|
function tryWrap(fn: (id: string, arg: any) => any): Function {
|
||||||
|
Loading…
Reference in New Issue
Block a user