模块热替换(HMR)可以在应用程序运行过程中交换、添加或删除模块,而无需完全重新加载。这可以在几个方面显著加快开发速度
让我们从不同的角度来理解 HMR 的具体工作原理...
以下步骤允许模块在应用程序中进行替换
你可以设置 HMR 自动执行此过程,或者选择要求用户交互才能进行更新。
除了常规资源外,编译器还需要发出一个“更新”以允许从旧版本更新到新版本。“更新”由两部分组成
manifest 包含新的编译哈希和所有更新的 chunk 列表。每个 chunk 都包含所有更新模块的新代码(或表示模块已移除的标志)。
编译器确保这些构建之间模块 ID 和 chunk ID 的一致性。它通常将这些 ID 存储在内存中(例如使用 webpack-dev-server),但也可以将其存储在 JSON 文件中。
HMR 是一个可选功能,仅影响包含 HMR 代码的模块。一个例子是通过 style-loader
打补丁样式。为了使补丁生效,style-loader
实现了 HMR 接口;当它通过 HMR 接收到更新时,它会将旧样式替换为新样式。
类似地,在模块中实现 HMR 接口时,你可以描述模块更新时应发生的情况。然而,在大多数情况下,不必在每个模块中编写 HMR 代码。如果一个模块没有 HMR 处理器,更新会向上冒泡。这意味着单个处理器可以更新完整的模块树。如果树中的单个模块更新,则整个依赖项集合会被重新加载。
有关 module.hot
接口的详细信息,请参阅HMR API 页面。
这里事情变得有点技术性... 如果你对内部细节不感兴趣,可以直接跳到HMR API 页面或HMR 指南。
对于模块系统运行时,会发出额外的代码来跟踪模块的 父级
和 子级
。在管理方面,运行时支持两种方法:check
和 apply
。
check
方法向更新 manifest 发出 HTTP 请求。如果此请求失败,则没有可用更新。如果成功,则会将更新的 chunk 列表与当前加载的 chunk 列表进行比较。对于每个已加载的 chunk,都会下载相应的更新 chunk。所有模块更新都存储在运行时中。当所有更新 chunk 都已下载并准备好应用时,运行时会切换到 ready
状态。
apply
方法将所有更新的模块标记为无效。对于每个无效模块,模块本身或其父级中需要有一个更新处理器。否则,无效标志会向上冒泡并同样使父级无效。每次冒泡都会继续,直到达到应用程序的入口点或具有更新处理器的模块(以先达到者为准)。如果它从入口点冒泡,则该过程失败。
之后,所有无效模块被处理(通过 dispose 处理器)并卸载。然后更新当前哈希,并调用所有 accept 处理器。运行时切换回 空闲
状态,一切恢复正常。
HMR 可以在开发中用作 LiveReload 的替代品。webpack-dev-server 支持热
模式,在此模式下,它会尝试使用 HMR 进行更新,然后再尝试重新加载整个页面。有关详细信息,请参阅模块热替换指南。