Просмотр исходного кода

fix(hmr): handle cases where instances with same id having different definitions

Evan You 6 лет назад
Родитель
Сommit
01b7e90eac
1 измененных файлов с 25 добавлено и 28 удалено
  1. 25 28
      packages/runtime-core/src/hmr.ts

+ 25 - 28
packages/runtime-core/src/hmr.ts

@@ -33,10 +33,7 @@ if (__DEV__) {
   } as HMRRuntime
 }
 
-interface HMRRecord {
-  comp: ComponentOptions
-  instances: Set<ComponentInternalInstance>
-}
+type HMRRecord = Set<ComponentInternalInstance>
 
 const map: Map<string, HMRRecord> = new Map()
 
@@ -47,21 +44,18 @@ export function registerHMR(instance: ComponentInternalInstance) {
     createRecord(id, instance.type as ComponentOptions)
     record = map.get(id)!
   }
-  record.instances.add(instance)
+  record.add(instance)
 }
 
 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 {
   if (map.has(id)) {
     return false
   }
-  map.set(id, {
-    comp,
-    instances: new Set()
-  })
+  map.set(id, new Set())
   return true
 }
 
@@ -70,7 +64,7 @@ function rerender(id: string, newRender?: Function) {
   if (!record) return
   // Array.from creates a snapshot which avoids the set being mutated during
   // updates
-  Array.from(record.instances).forEach(instance => {
+  Array.from(record).forEach(instance => {
     if (newRender) {
       instance.render = newRender as InternalRenderFunction
     }
@@ -85,22 +79,29 @@ function rerender(id: string, newRender?: Function) {
 function reload(id: string, newComp: ComponentOptions) {
   const record = map.get(id)
   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
   // 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) {
-      // 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
       // don't end up forcing the same parent to re-render multiple times.
       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 {