模块热替换

模块热替换(HMR)可以在应用程序运行过程中交换、添加或删除模块,而无需完全重新加载。这可以在几个方面显著加快开发速度

  • 保留在完全重新加载过程中丢失的应用程序状态。
  • 仅更新已更改的内容,从而节省宝贵的开发时间。
  • 当源代码中的 CSS/JS 发生修改时,立即更新浏览器,这几乎与直接在浏览器开发者工具中修改样式相当。

工作原理

让我们从不同的角度来理解 HMR 的具体工作原理...

在应用程序中

以下步骤允许模块在应用程序中进行替换

  1. 应用程序请求 HMR 运行时检查更新。
  2. 运行时异步下载更新并通知应用程序。
  3. 应用程序随后请求运行时应用更新。
  4. 运行时同步应用更新。

你可以设置 HMR 自动执行此过程,或者选择要求用户交互才能进行更新。

在编译器中

除了常规资源外,编译器还需要发出一个“更新”以允许从旧版本更新到新版本。“更新”由两部分组成

  1. 更新后的 manifest (JSON)
  2. 一个或多个更新后的 chunk (JavaScript)

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 指南

对于模块系统运行时,会发出额外的代码来跟踪模块的 父级子级。在管理方面,运行时支持两种方法:checkapply

check 方法向更新 manifest 发出 HTTP 请求。如果此请求失败,则没有可用更新。如果成功,则会将更新的 chunk 列表与当前加载的 chunk 列表进行比较。对于每个已加载的 chunk,都会下载相应的更新 chunk。所有模块更新都存储在运行时中。当所有更新 chunk 都已下载并准备好应用时,运行时会切换到 ready 状态。

apply 方法将所有更新的模块标记为无效。对于每个无效模块,模块本身或其父级中需要有一个更新处理器。否则,无效标志会向上冒泡并同样使父级无效。每次冒泡都会继续,直到达到应用程序的入口点或具有更新处理器的模块(以先达到者为准)。如果它从入口点冒泡,则该过程失败。

之后,所有无效模块被处理(通过 dispose 处理器)并卸载。然后更新当前哈希,并调用所有 accept 处理器。运行时切换回 空闲 状态,一切恢复正常。

开始

HMR 可以在开发中用作 LiveReload 的替代品。webpack-dev-server 支持模式,在此模式下,它会尝试使用 HMR 进行更新,然后再尝试重新加载整个页面。有关详细信息,请参阅模块热替换指南

6 贡献者

kryptokinghtSpaceK33zsokraGRardBrouzbeh84skipjack