2022-04-09
Webpack
0

目录

双向通讯机制
webpack监听文件变化
通知浏览器刷新
hotApply 热更新模块替换
参考

在前端开发中,webpack-dev-server是一个非常常用的工具,它可以为我们提供热更新的功能,使得开发变得更加高效。那么,webpack-dev-server是如何实现热更新的呢?

在本文中,我们将深入探讨webpack-dev-server的热更新原理。我们将介绍热更新的概念、如何在webpack-dev-server中启用热更新、热更新的工作原理以及如何使用热更新提高前端开发效率。无论您是初学者还是有一定经验的前端工程师,本文都将为您提供有价值的信息。

双向通讯机制

webpack-dev-server启动时,会先启动本地静态资源服务,然后在启动websocket服务,

websocket服务此时会和本地静态资源服务建立连接,

接着在webpack entry里面加入本地服务和websocket通讯的相关代码

jsx
// 修改后的entry入口 // 在入口新增两个文件,就会一起打包到bundle文件中去,在线上运行。 { entry: { main: [ // 上面获取的clientEntry 'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080', // 上面获取的hotEntry 'xxx/node_modules/webpack/hot/dev-server.js', // 开发配置的入口 './src/main.js' ] } }

这样就实现了双向通讯机制

webpack监听文件变化

jsx
webpack-dev-server:负责启动服务和前置准备工作 webpack-dev-middleware:负责本地文件的编译和输出以及监听。

webpack-dev-middleware监听到文件发生变化时,就会给浏览器发送ok和hash事件

还记得我们上面说的修改entry入口么? 这个文件会被打包到bundle.js中,并运行在浏览器中。

xxx/node_modules/webpack-dev-server/client/index.js?[http://localhost:8080](https://link.juejin.cn/?target=http%3A%2F%2Flocalhost%3A8080)

看一下这个核心代码:

jsx
// webpack-dev-server/client/index.js var socket = require('./socket'); var onSocketMessage = { hash: function hash(_hash) { // 更新currentHash值 status.currentHash = _hash; }, ... ... ok: function ok() { sendMessage('Ok'); // 进行更新检查等操作 reloadApp(options, status); }, } socket(socketUrl, onSocketMessage); // webpack-dev-server/client/util/reloadApp.js function reloadApp() { if (hot) { log.info('[WDS] App hot update...'); // hotEmitter其实就是EventEmitter的实例 var hotEmitter = require('webpack/hot/emitter'); hotEmitter.emit('webpackHotUpdate', currentHash); } }

reloadApp()方法中,利用了node.jsEventEmitter,发出webpackHotUpdate消息,发出了这个消息之后,webpack接下来会做什么呢?

通知浏览器刷新

我们之前entry入口中,除了新增 xxx/node_modules/webpack-dev-server/client/index.js?[http://localhost:8080](https://link.juejin.cn/?target=http%3A%2F%2Flocalhost%3A8080), 还有新增了一个文件:xxx/node_modules/webpack/hot/dev-server.js

那我们看看这个文件的内容:

jsx
// node_modules/webpack/hot/dev-server.js var check = function check() { module.hot.check(true) .then(function(updatedModules) { // 容错,直接刷新页面 if (!updatedModules) { window.location.reload(); return; } // 热更新结束,打印信息 if (upToDate()) { log("info", "[HMR] App is up to date."); } }) .catch(function(err) { window.location.reload(); }); }; var hotEmitter = require("./emitter"); hotEmitter.on("webpackHotUpdate", function(currentHash) { lastHash = currentHash; check(); });

但是module.hot.check这个是来自哪里呢?他是来自webpackConfig的hot值

利用上一次保存的hash值,调用 hotDownloadManifest 发送 xxx/hash-update.json的ajax请求,调用hotDownloadUpdateChunl 发送 xxx/hash.hot-update.js请求(JSONP)

hotApply 热更新模块替换

热更新的核心在 hotApply 这个函数中。具体做了:

  • 删除过期的模块,就是需要替换的模块 (通过hotUpdate可以找到旧模块)
jsx
var queue = outdatedModules.slice(); while (queue.length > 0) { moduleId = queue.pop(); // 从缓存中删除过期的模块 module = installedModules[moduleId]; // 删除过期的依赖 delete outdatedDependencies[moduleId]; // 存储了被删掉的模块id,便于更新代码 outdatedSelfAcceptedModules.push({ module: moduleId }); } 复制代码
  • 将新的模块添加到modules中
jsx
appliedUpdate[moduleId] = hotUpdate[moduleId]; for (moduleId in appliedUpdate) { if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) { modules[moduleId] = appliedUpdate[moduleId]; } } 复制代码
  • 通过__webpack_require__执行相关模块的代码
jsx
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) { var item = outdatedSelfAcceptedModules[i]; moduleId = item.module; try { // 执行最新的代码 __webpack_require__(moduleId); } catch (err) { // ...容错处理 } }

参考

webpack 热更新原理

本文作者:BARM

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!