]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hmr): handle cases where instances with same id having different definitions
authorEvan You <yyx990803@gmail.com>
Sun, 3 May 2020 17:52:09 +0000 (13:52 -0400)
committerEvan You <yyx990803@gmail.com>
Sun, 3 May 2020 17:52:09 +0000 (13:52 -0400)
packages/runtime-core/src/hmr.ts

index 4a37f4f8728c7d9919028dfb5c6e47d21f6c7f36..0e2dfe750bdff69e4747e30a386204c99fa18131 100644 (file)
@@ -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 {