热模块替换

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

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

工作原理

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

在应用程序中

以下步骤允许在应用程序中交换模块。

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

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

在编译器中

除了普通资产外,编译器还需要发出“更新”以允许从先前版本更新到新版本。“更新”包含两个部分

  1. 更新的 清单 (JSON)
  2. 一个或多个更新的块 (JavaScript)

清单包含新的编译哈希值和所有更新块的列表。这些块中的每一个都包含所有更新模块的新代码(或指示模块已删除的标志)。

编译器确保这些构建中的模块 ID 和块 ID 保持一致。它通常将这些 ID 存储在内存中(例如,使用 webpack-dev-server),但也可以将它们存储在 JSON 文件中。

在模块中

HMR 是一种选择性功能,它只影响包含 HMR 代码的模块。一个例子是通过 style-loader 修补样式。为了使修补工作,style-loader 实现了 HMR 接口;当它通过 HMR 接收更新时,它会用新样式替换旧样式。

类似地,在模块中实现 HMR 接口时,您可以描述模块更新时应该发生什么。但是,在大多数情况下,并非必须在每个模块中编写 HMR 代码。如果模块没有 HMR 处理程序,更新会冒泡。这意味着单个处理程序可以更新完整的模块树。如果树中的单个模块被更新,则整个依赖项集将被重新加载。

有关 module.hot 接口的详细信息,请参阅 HMR API 页面

在运行时

这里事情变得有点技术性……如果您对内部机制不感兴趣,可以跳到 HMR API 页面HMR 指南

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

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

apply 方法将所有更新的模块标记为无效。对于每个无效模块,需要在模块或其父级中有一个更新处理程序。否则,无效标记会冒泡并使父级也失效。每个冒泡会一直持续到应用程序的入口点或具有更新处理程序的模块为止(以先到达者为准)。如果它从入口点冒泡,则进程将失败。

之后,所有无效模块都会被处置(通过处置处理程序)并卸载。然后更新当前哈希值,并调用所有 accept 处理程序。运行时切换回 idle 状态,一切照常继续。

入门

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

6 位贡献者

kryptokinghtSpaceK33zsokraGRardBrouzbeh84skipjack