了不起的 Webpack HMR 学习指南(含源码分析) 上

简介: 了不起的 Webpack HMR 学习指南(含源码分析) 上


最近原创文章回顾😊:

学习章节:《Webpack HMR 原理解析》

一、HMR 介绍

Hot Module Replacement(以下简称:HMR 模块热替换)是 Webpack 提供的一个非常有用的功能,它允许在 JavaScript 运行时更新各种模块,而无需完全刷新

Hot Module Replacement (or HMR) is one of the most useful features offered by webpack. It allows all kinds of modules to be updated at runtime without the need for a full refresh. --《Hot Module Replacement》

当我们修改代码并保存后,Webpack 将对代码重新打包,HMR 会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。

HMR 主要通过以下几种方式,来显著加快开发速度:

  • 保留在完全重新加载页面时丢失的应用程序状态;
  • 只更新变更内容,以节省宝贵的开发时间;
  • 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。

需要注意:HMR 不适用于生产环境,这意味着它应当只在开发环境使用。

二、HMR 使用方式

在 Webpack 中启用 HMR 功能比较简单:

1. 方式一:使用 devServer

1.1 设置 devServer 选项

只需要在 webpack.config.js 中添加 devServer 选项,并设置 hot 值为 true ,并使用HotModuleReplacementPluginNamedModulesPlugin (可选)两个 Plugins :

// webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
 entry: './index.js',
 output: {
  filename: 'bundle.js',
  path: path.join(__dirname, '/')
 },
+ devServer: {
+  hot: true,   // 启动模块热更新 HMR
+   open: true,  // 开启自动打开浏览器页面
+ },
  plugins: [
+   new webpack.NamedModulesPlugin(),
+   new webpack.HotModuleReplacementPlugin()
  ]
}

1.2 添加 scripts

然后在 package.json 中为 scripts 命令即可:

// package.json
{
  // ...
  "scripts": {
+    "start": "webpack-dev-server"
  },
  // ...
}

2. 方式二、使用命令行参数

另一种是通过添加 --hot 参数来实现。添加 --hot 参数后,devServer 会告诉 Webpack 自动引入 HotModuleReplacementPlugin ,而不需要我们手动引入。

另外常常也搭配 --open 来自动打开浏览器到页面。

这里移除掉前面添加的两个 Plugins :

// webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
 // ...
- plugins: [
-   new webpack.NamedModulesPlugin(),
-   new webpack.HotModuleReplacementPlugin()
- ]
}

然后修改 package.json 文件中的 scripts 配置:

// package.json
{
  // ...
  "scripts": {
-    "start": "webpack-dev-server"
+    "start": "webpack-dev-server --hot --open"
  },
  // ...
}

3. 简单示例

基于上述配置,我们简单实现一个场景: index.js 文件中导入 hello.js 模块,当 hello.js 模块发生变化时, index.js 将更新模块。

模块代码如下实现:

// hello.js
export default () => 'hi leo!';
// index.js
import hello from './hello.js'
const div = document.createElement('div');
div.innerHTML = hello();
document.body.appendChild(div);

然后在 index.html 中导入打包后的 JS 文件,并执行 npm start 运行项目:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
</head>
<body>
 <div>了不起的 Webpack HMR 学习指南</div>
 <script src="bundle.js"></script>
</body>
</html>

4. 实现监听更新

当我们通过 HotModuleReplacementPlugin  插件启用了 HMR,则它的接口将被暴露在全局 module.hot  属性下面。通常,可以先检查这个接口是否可访问,然后再开始使用它。

举个例子,你可以这样 accept  一个更新的模块:

if (module.hot) {
  module.hot.accept('./library.js', function() {
    // 使用更新过的 library 模块执行某些操作...
  })
}

关于 module.hot 更多 API ,可以查看官方文档《Hot Module Replacement API》

回到上面示例,我们测试更新模块的功能。

这时我们修改 index.js 代码,来监听 hello.js 模块中的更新:

import hello from './hello.js';
const div = document.createElement('div');
div.innerHTML = hello();
document.body.appendChild(div);
+ if (module.hot) {
+   module.hot.accept('./hello.js', function() {
+     console.log('现在在更新 hello 模块了~');
+     div.innerHTML = hello();
+   })
+ }

然后修改 hello.js 文件内容,测试效果:

- export default () => 'hi leo!';
+ export default () => 'hi leo! hello world';

当我们保存代码时,控制台输出 "现在在更新 hello模块了~" ,并且页面中 "hi leo!" 也更新为 "hi leo! hello world" ,证明我们监听到文件更新了。


简单 Webpack HMR 使用方式就介绍到这,更多介绍,还请阅读官方文档《Hot Module Replacement》

5. devServer 常用配置和技巧

5.1 常用配置

根据目录结构的不同,contentBaseopenPage 参数要配置合适的值,否则运行时应该不会立刻访问到你的首页。 同时要注意你的 publicPath,静态资源打包后生成的路径是一个需要思考的点,取决于你的目录结构。

devServer: {
  contentBase: path.join(__dirname, 'static'),    // 告诉服务器从哪里提供内容(默认当前工作目录)
  openPage: 'views/index.html',  // 指定默认启动浏览器时打开的页面
  index: 'views/index.html',  // 指定首页位置
  watchContentBase: true, // contentBase下文件变动将reload页面(默认false)
  host: 'localhost', // 默认localhost,想外部可访问用'0.0.0.0'
  port: 8080, // 默认8080
  inline: true, // 可以监控js变化
  hot: true, // 热启动
  open: true, // 启动时自动打开浏览器(指定打开chrome,open: 'Google Chrome')
  compress: true, // 一切服务都启用gzip 压缩
  disableHostCheck: true, // true:不进行host检查
  quiet: false,
  https: false,
  clientLogLevel: 'none',
  stats: { // 设置控制台的提示信息
    chunks: false,
    children: false,
    modules: false,
    entrypoints: false, // 是否输出入口信息
    warnings: false,
    performance: false, // 是否输出webpack建议(如文件体积大小)
  },
  historyApiFallback: {
    disableDotRule: true,
  },
  watchOptions: {
    ignored: /node_modules/, // 略过node_modules目录
  },
  proxy: { // 接口代理(这段配置更推荐:写到package.json,再引入到这里)
    "/api-dev": {
      "target": "http://api.test.xxx.com",
      "secure": false,
      "changeOrigin": true,
      "pathRewrite": { // 将url上的某段重写(例如此处是将 api-dev 替换成了空)
        "^/api-dev": ""
      }
    }
  },
  before(app) { },
}

5.2 技巧1:文件形式输出 dev-server 代码

dev-server 输出的代码通常在内存中,但也可以写入硬盘,产出实体文件:

devServer:{
  writeToDisk: true,
}

通常可以用于代理映射文件调试,编译时会产出许多带 hash 的 js 文件,不带 hash 的文件同样也是实时编译的

5.3 技巧2:默认使用本地 IP 启动服务

有的时候,启动服务时,想要默认使用本地的 ip 地址打开:

devServer:{
  disableHostCheck: true, // true:不进行host检查
  // useLocalIp: true, // 建议不在这里配置
  // host: '0.0.0.0', // 建议不在这里配置
}

同时还需要将 host 配置为 0.0.0.0,这个配置建议在 scripts 命令中追加,而非在配置中写死,否则将来不想要这种方式往回改折腾,取巧一点,配个新命令:

"dev-ip": "yarn run dev --host 0.0.0.0 --useLocalIp"

5.4 技巧3:指定启动的调试域名

有时启动的时候希望是指定的调试域名,例如:local.test.baidu.com

devServer:{
  open: true,
  public: 'local.test.baidu.com:8080', // 需要带上端口
  port: 8080,
}

同时需要将 127.0.0.1 修改为指定的 host,可以借助 iHost 等工具去修改,各个工具大同小异,格式如下:

127.0.0.1 local.test.baidu.com

服务启动后将自动打开 local.test.baidu.com:8080 访问

5.5 技巧4:启动 gzip 压缩

devServer:{
  compress: true,
}

三、HMR 基本原理介绍

从前面介绍中,我们知道:HMR 主要功能是会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面

那么,Webpack 编译源码所产生的文件变化在编译时,替换模块实现在运行时,两者如何联系起来?


带着这两个问题,我们先简单看下 HMR 核心工作流程(简化版):

HMR 工作流程图.png

接下来开始 HMR 工作流程分析:

  1. 当 Webpack(Watchman) 监听到项目中的文件/模块代码发生变化后,将变化通知 Webpack 中的构建工具(Packager)即 HMR Plugin;
  2. 然后经过 HMR Plugin 处理后,将结果发送到应用程序(Application)的运行时框架(HMR Runtime);
  3. 最后由 HMR Runtime 将这些发生变化的文件/模块更新(新增/删除或替换)到模块系统中。

其中,HMR Runtime 是构建工具在编译时注入的,通过统一的 Module ID 将编译时的文件与运行时的模块对应起来,并且对外提供一系列 API 供应用层框架(如 React)调用。

💖注意💖:建议先理解上面这张图的大致流程,在进行后续阅读。放心,我等着大家~😃


目录
相关文章
|
6月前
|
JSON JavaScript 前端开发
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)(下)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
89 2
|
JSON 前端开发 JavaScript
前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(一)
前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(一)
588 0
|
1月前
|
JavaScript
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
这篇文章是关于如何在webpack环境中配置Vue.js,包括安装Vue.js、解决报错、理解el与template的区别、使用SPA模式、抽离模板为对象、封装为单独的js文件、安装vue-loader时遇到的问题及解决方案,以及整个过程的总结。
71 2
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
|
1月前
|
JavaScript
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
这篇文章介绍了如何将webpack的配置文件分离成开发环境和生产环境的配置文件,以提高打包效率。
46 1
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
|
1月前
|
JavaScript 前端开发 Java
webpack学习一:什么是模块化开发,什么是webpack,以及二者之间的关系。
这篇文章介绍了模块化开发的概念、历史和实现方式,以及webpack作为一个现代JavaScript应用的静态模块打包工具,它如何帮助我们将ES6等高级语法打包成浏览器可以识别的低级语法,并解释了npm在webpack安装和使用中的作用。
40 1
webpack学习一:什么是模块化开发,什么是webpack,以及二者之间的关系。
|
19天前
|
监控 前端开发 JavaScript
Webpack 中 HMR 插件的工作原理
【10月更文挑战第23天】可以进一步深入探讨 HMR 工作原理的具体细节、不同场景下的应用案例,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解 HMR 插件的工作原理,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的 HMR 技术和最佳实践。
|
1月前
|
移动开发 JavaScript 前端开发
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
这篇文章主要介绍了如何通过配置Webpack的插件,如HtmlWebpackPlugin、uglifyjs-webpack-plugin和webpack-dev-server,来简化前端开发流程。
39 0
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
|
6月前
|
Web App开发 JSON 前端开发
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
230 0
|
6月前
|
存储 前端开发 JavaScript
Webpack【Webpack中模式(Mode)、Webpack中使用DevServer、Webpack中devtool增强调试过程】(二)-全面详解(学习总结---从入门到深化)
Webpack【Webpack中模式(Mode)、Webpack中使用DevServer、Webpack中devtool增强调试过程】(二)-全面详解(学习总结---从入门到深化)
163 0
|
6月前
|
JSON 前端开发 JavaScript
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)(上)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
89 0