webpack5 热更新从配置到原理
概述
项目里用到的刷新方案有两种,一般为了区分称为热更新和热重载
- 热重载 : 顾名思义重新加载,使用
window.location.reload()
刷新界面以达到更新的目的 - 热更新:只更新修改的文件。不会刷新界面。
Hot Module Replacement
(或 HMR)是webpack
提供的最有用的功能之一。它允许在运行时更新各种模块,而无需完全刷新。 - 官网
不管热重载和热更新都能很大程度提高我们的开发效率。下面将由浅入深的的梳理下热更新的知识点,而热重载相关知识比较简单,会在热更新的解释中提及。
了解热更新的价值
在我的平时工作中在了解热更新的机制后,在一下几个方面会有用到的机会。
- 搭建自己的项目,在
webpack
配置中使用 - 搭建自己的cli,将自己的
webpack
配置封装到cli中,参考vue-cli
。 - 搭建自己的SSR框架时,参考
Nuxt
。 - 面试涨薪,走上人生巅峰。
热更新的实现
此文不赘述官网上面的相关知识了。下面主要介绍 通过 Node.js API 来搭建 HMR
的过程的拓展。
官网 是将 Webpack Dev Server
与 Node.js API
一起使用,来实现 HMR
的搭建的,为了更好的理解 HMR
的原理,这里我们使用 更深一步的 middleware
plugin
和 Node.js API
来实现 HMR
。
首先简单了解下将要用到的几种 middleware
和 plugin
- webpack-hot-middleware: 该模块只关注将浏览器客户端连接到 webpack 服务器并接收更新的机制。
- webpack-dev-middleware: 是一个容器(wrapper),它可以把webpack处理后的文件传递给一个服务器(server)。
- webpack.HotModuleReplacementPlugin: 用于生成热更新的相关文件。
下面进行实操将这些 middleware
和 plugin
进行组合,以实现 HMR
。首先我们基于官网的例子, 新建出以下所示的目录具体代码的github地址
├─package.json
├─yarn.lock
├─src
| ├─index.js - 同官网的例子
| └print.js
├─server
| └dev-server.js
├─bundle
| └webpack.config.js
package.json
"express": "^4.17.1",
"html-webpack-plugin": "^5.3.1",
"webpack": "^5.39.1",
"webpack-cli": "^4.7.2",
"webpack-dev-middleware": "^5.0.0",
"webpack-hot-middleware": "^2.25.0"
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "development",
entry: {
index: './src/index.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist'),
clean: true,
},
plugins: [
new HtmlWebpackPlugin(),
]
};
dev-server.js
const config = require("../bundle/webpack.config");
const express = require('express');
const app = express();
const webpack = require("webpack");
// 当前的环境是开发环境
// 修改入口文件,用于热更新通信
config.entry.index = ["webpack-hot-middleware/client", config.entry.index];
// 用与生成热更新的 lastHash.hot-update.json 和 chunkID.lastHash.hot-update.js
config.plugins.push(new webpack.HotModuleReplacementPlugin());
// 准备好热更新的环境之后开始编译
const compiler = webpack(config);
// webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。
const devMiddleware = require("webpack-dev-middleware")(compiler, {
publicPath: config.output.publicPath,
serverSideRender: true
});
app.use(devMiddleware)
app.use(require("webpack-hot-middleware")(compiler));
compiler.hooks.done.tap("done", stats => {
const info = stats.toJson();
if (stats.hasWarnings()) {
console.warn(info.warnings);
}
if (stats.hasErrors()) {
console.error(info.errors);
return;
}
console.log("打包完成");
});
app.listen(9527, () => {
console.log("Your app is running", 9527);
});
这个时候我们就实现了类似官网的 HMR
下面我们就基于这个 demo
来浅谈下 HRM
的原理。
热更新的原理
热更新在编译阶段和界面上的体现
当我们编译的时候,可以直观的看到生成了 index.37b9afad78a811b0d250.hot-update.js
和index.37b9afad78a811b0d250.hot-update.json
这两个与 HMR
相关的两个文件。在浏览器上,发现发送了两个请求
分别用来获取这两个文件。返回的内容如下
index.37b9afad78a811b0d250.hot-update.json
{"c":["index"],"r":[],"m":[]}
index.37b9afad78a811b0d250.hot-update.js
self["webpackHotUpdateuse"]("index", {
"./src/print.js":
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
"default": () => (/* binding */ printMe)});
function printMe() {
console.log('Updating print.js2..');
}//# sourceURL=webpack://use/./src/print.js?");
})},
function(__webpack_require__) {
"use strict";
(() => {
__webpack_require__.h = () => ("f4e40048658a6c48ff32")
})();
}
);
看到上面的现象之后,大概有许多疑惑,下面带着这些疑惑继续往下看。
热更新的原理讲解
带着上面的问题,我们来探索热更新的意义,首先我们看下热更新的整体流程
- 首先启动
dev-server
, 往当前的options.entry
中插入相关的 SSE 相关的代码。
注入 HTML 5 服务器发送事件 ( SSE, server-sent event ) ,和 心跳检测的相关机制,主要是为了服务器和客户端的通信,当服务器文件发生变化的时候能够方便通知到客户端。
- 监听
webpack done
的compiler.hooks.done.tap
节点从stats
中获取到最新的hash
值,传递给客户端,如果只是做热重载到这步就可以结束了,使用window.location.reload();
- 客户端获取
lastHash.hot-update.json
文件,获取到当前更新了的文件的 chunkId 。 - 根据当前更新的
chunkId
去拿chunkID.lastHash.hot-update.js
文件 - 执行
webpackHotUpdate
, 从 现有的cache
中 找到当前的 chunkId 对应的旧的数据信息,进行更新并执行当前最新的chunk
代码,以便于更新cache
,接着执行对应的hot.accept
代码来实现render
操作。
总结
本文没有使用 webpackDevServer
,其实 webpackDevServer
就是内置了 Webpack-dev-middleware
和 Express
服务器,以及利用 websocket
替代 SSE
来实现webpack-hot-middleware
的逻辑。原理的部分也是帮助大家从整体上认识了 HMR
。希望通过这篇文章,可以将热更新活学活用,如果需要了解每一步的具体细节,可以按照这个思路去看看源码。
转载自:https://juejin.cn/post/6976082291428704292