前端

webpack 和 vite 中 HMR 的区别

什么是 HMR ?

HMR 是热模块替换(Hot Module Replacement)的缩写。它允许在应用程序运行时更新模块,而无需完全刷新页面。webpack 和 vite 都支持 HMR,但它们的实现方式和原理不同。

webpack 的 HMR 实现

Webpack 采用了传统的打包方式,在开发模式下,Webpack 会将代码打包成模块,并运行一个 webpack-dev-server。当文件发生变化时,Webpack 重新编译这个有变动的模块,并通过 WebSocket 将模块更新通知到浏览器,浏览器通过 HMR runtime 接收更新,不需要刷新整个页面。

传统打包方式:将项目中的所有依赖文件(如 JavaScript、CSS、图片等)通过一系列的编译、解析和转换步骤,生成一个或多个 bundle 文件。

  1. 文件监听: webpack 使用 chokidar 库监听文件变化。
  2. 模块重编译: 当文件变化时,webpack 重新编译变化的模块。
  3. 生成更新 manifest: 生成包含变更模块 ID 的 manifest。
  4. 通过 WebSocket 发送更新消息
  5. 客户端接收更新并应用:
    • HMR runtime 通过 manifest 文件找到变化的模块,并根据模块 ID 定位受影响的模块。
    • 遍历模块依赖图,找出依赖这些模块的其他模块,并确保这些模块的更新不会破坏整体应用。
    • 将新的模块代码替换旧的代码,尽可能保留模块的运行状态。

vite 的 HMR 实现

Vite 依赖 ES Modules (ESM) 和浏览器的原生模块化支持,在开发模式下不进行预打包,而是按需加载文件。Vite 直接利用浏览器来加载 ES 模块,并在文件发生变化时,替换相关模块。

具体实现流程

  1. 文件监听:vite 也是使用 chokidar 库监听文件变化。

  2. 模块重编译:当文件变化时,vite 只需要重新编译变化的模块,不需要重新打包整个应用。

  3. 通过 WebSocket 发送更新:vite 通过 WebSocket 将更新的模块路径发送给客户端。

  4. 客户端接收并应用更新:客户端的 HMR runtime 接收更新信息,直接通过 import() 加载更新的模块。

webpack 和 vite HMR 的主要区别

  1. 编译范围

    • Webpack: 当一个模块发生变化时,Webpack 会重新编译整个模块的依赖图,包括其依赖的模块和其他可能受影响的模块。这意味着它通常需要处理大量的编译任务,尤其是在大型项目中,编译范围会相对较大。
    • Vite: Vite 依赖 ES 模块的按需加载特性,只重新编译和更新发生变化的模块,而不需要重新构建整个依赖图。这使得编译更加轻量和高效。
  2. 更新粒度

    • Webpack: Webpack 使用 chunk(代码块)作为更新单位。一个 chunk 通常包含多个模块的代码,因此即使是一个小模块的变化,也可能需要更新整个 chunk。
    • Vite: Vite 以 ES 模块为单位进行更新。模块的边界清晰,变化只影响单个模块,极大地减少了不必要的代码更新。模块的按需加载使得更新粒度更加精细。
  3. 更新速度

    • Webpack: 由于需要重新打包整个模块依赖图,Webpack 的更新速度在大型项目中会明显变慢。尽管通过一些优化技术(如增量构建、缓存)可以提升速度,但整体性能仍不如 Vite。
    • Vite: Vite 的更新速度非常快,因为它直接利用浏览器原生的 ES 模块系统进行模块加载和更新,只重新编译变化的部分,无需进行打包或预处理。因此,Vite 在 HMR 方面的响应时间几乎是瞬时的。
  4. 配置复杂度

    • Webpack: Webpack 的配置灵活但复杂。为了实现 HMR,通常需要额外的插件和 loader 配置,且根据具体的项目需求,这些配置可能需要进一步调整和优化。
    • Vite: Vite 追求简洁,开箱即用。Vite 内置了 HMR 功能,几乎不需要额外的配置。对于大多数项目,开发者可以直接享受到高效的 HMR 而不需要手动配置复杂的工具链。
Previous
CSS伪元素的妙用