webpack HMR

article/2025/10/7 14:27:05

HMR或者hot模式下,启动webpack会在浏览器与服务器之间会建立一个websocket连接,使得浏览器可以和服务端建立全双工通信;当应用程序的代码更新时,会要求HMR runtime检查更新,有更新时,在websoket连接中会返回原文件的hash以及更新后代码的hash,并在[oldhash].hot-update.json文件中返回如下结构:

{h: "fe94d54f9adaaa2831b2",	// new hash, h: hashc: {						// c: chunk, updated chunckapp: true}
}

json中包含了新的模块标识符的hash以及需要更新的chunk name。拿到新的hash后,还需要更新后的chunk的代码,通过[newhash].hot-update.js拿到对应更新后的代码;最后根据新的hash请求重新打包出来的bundle.js文件;并重新建立websocket以便监测下一次更新;

在这里插入图片描述

图1 - websocket通信

在这里插入图片描述

图2 —— websocket获取更新后的文件hash

在这里插入图片描述

图3 —— [hash].hot-update.json

// http://localhost:3000/app.9b34b0372e9214ced0b7.hot-update.js
// [newhash].hot-update.jswebpackHotUpdate("app",{/***/ "./index.js":
/*!******************!*\!*** ./index.js ***!\******************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {"use strict";var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();var _react = __webpack_require__(/*! react */ "./node_modules/react/index.js");var _react2 = _interopRequireDefault(_react);var _reactDom = __webpack_require__(/*! react-dom */ "./node_modules/react-dom/index.js");__webpack_require__(/*! ./style.css */ "./style.css");__webpack_require__(/*! ./index.css */ "./index.css");var _console = __webpack_require__(/*! ./console.js */ "./console.js");var _console2 = _interopRequireDefault(_console);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }var App = function (_Component) {_inherits(App, _Component);function App(props) {_classCallCheck(this, App);var _this = _possibleConstructorReturn(this, (App.__proto__ || Object.getPrototypeOf(App)).call(this, props));_this.handleClick = _this.handleClick.bind(_this);return _this;}_createClass(App, [{key: 'handleClick',value: function handleClick() {var fun = function fun() {return console.log('123');};fun();}}, {key: 'render',value: function render() {return _react2.default.createElement('div',{ onClick: _console2.default },'Hello World, I am changed again again...');}}]);return App;
}(_react.Component);(0, _reactDom.render)(_react2.default.createElement(App, null), document.getElementById('root'));if (true) {module.hot.accept(/*! ./console.js */ "./console.js", function () {(0, _console2.default)();});
}/***/ })})
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9pbmRleC5qcyJdLCJuYW1lcyI6WyJBcHAiLCJwcm9wcyIsImhhbmRsZUNsaWNrIiwiYmluZCIsImZ1biIsImNvbnNvbGUiLCJsb2ciLCJteUNvbnNvbGUiLCJDb21wb25lbnQiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwibW9kdWxlIiwiaG90IiwiYWNjZXB0Il0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7OztBQUFBOzs7O0FBQ0E7O0FBRUE7O0FBQ0E7O0FBQ0E7Ozs7Ozs7Ozs7OztJQUVPQSxHOzs7QUFDTCxlQUFhQyxLQUFiLEVBQW9CO0FBQUE7O0FBQUEsMEdBQ2JBLEtBRGE7O0FBRW5CLFVBQUtDLFdBQUwsR0FBbUIsTUFBS0EsV0FBTCxDQUFpQkMsSUFBakIsT0FBbkI7QUFGbUI7QUFHbkI7Ozs7a0NBQ2M7QUFDZCxVQUFJQyxNQUFNLFNBQU5BLEdBQU07QUFBQSxlQUFNQyxRQUFRQyxHQUFSLENBQVksS0FBWixDQUFOO0FBQUEsT0FBVjtBQUNBRjtBQUNBOzs7NkJBQ1E7QUFDVCxhQUFRO0FBQUE7QUFBQSxVQUFLLFNBQVNHLGlCQUFkO0FBQUE7QUFBQSxPQUFSO0FBQ0E7Ozs7RUFYaUJDLGdCOztBQWNuQixzQkFDQyw4QkFBQyxHQUFELE9BREQsRUFFQ0MsU0FBU0MsY0FBVCxDQUF3QixNQUF4QixDQUZEOztBQUtBLElBQUlDLElBQUosRUFBZ0I7QUFDZkEsU0FBT0MsR0FBUCxDQUFXQyxNQUFYLENBQWtCLGtDQUFsQixFQUFrQyxZQUFZO0FBQzdDO0FBQ0EsR0FGRDtBQUdBLEMiLCJmaWxlIjoiYXBwLjliMzRiMDM3MmU5MjE0Y2VkMGI3LmhvdC11cGRhdGUuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtDb21wb25lbnR9IGZyb20gJ3JlYWN0JztcclxuaW1wb3J0IHtyZW5kZXJ9IGZyb20gJ3JlYWN0LWRvbSc7XHJcblxyXG5pbXBvcnQgJy4vc3R5bGUuY3NzJ1xyXG5pbXBvcnQgJy4vaW5kZXguY3NzJ1x0XHJcbmltcG9ydCBteUNvbnNvbGUgZnJvbSAnLi9jb25zb2xlLmpzJ1xyXG5cclxuIGNsYXNzIEFwcCBleHRlbmRzIENvbXBvbmVudCB7XHJcbiBcdGNvbnN0cnVjdG9yIChwcm9wcykge1xyXG4gXHRcdHN1cGVyKHByb3BzKTtcclxuIFx0XHR0aGlzLmhhbmRsZUNsaWNrID0gdGhpcy5oYW5kbGVDbGljay5iaW5kKHRoaXMpO1xyXG4gXHR9XHJcbiBcdGhhbmRsZUNsaWNrICgpIHtcclxuIFx0XHRsZXQgZnVuID0gKCkgPT4gY29uc29sZS5sb2coJzEyMycpO1xyXG4gXHRcdGZ1bigpO1xyXG4gXHR9XHJcblx0cmVuZGVyICgpIHtcclxuXHRcdHJldHVybiAgPGRpdiBvbkNsaWNrPXtteUNvbnNvbGV9PkhlbGxvIFdvcmxkLCBJIGFtIGNoYW5nZWQgYWdhaW4gYWdhaW4uLi48L2Rpdj5cclxuXHR9XHJcbn1cclxuXHJcbnJlbmRlcihcclxuXHQ8QXBwLz4sXHJcblx0ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jvb3QnKVxyXG4pXHJcblxyXG5pZiAobW9kdWxlLmhvdCkge1xyXG5cdG1vZHVsZS5ob3QuYWNjZXB0KCcuL2NvbnNvbGUuanMnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRteUNvbnNvbGUoKTtcclxuXHR9KVxyXG59Il0sInNvdXJjZVJvb3QiOiIifQ==

sourcemap定义了源代码与编译后代码的映射关系,从而可以方便我们debug,其结构如下,包括version、sources、names、mappings、file、sourcesContent及sourceRoot字段;sources指定需要映射的文件,names和mappings则建立其中关键属性的对应映射关系,mappdings使用VLQ编码指定了具体字段的位置信息;file字段指定需要更新的js文件[hash].hot-update.js;sourceContent则是做mapping的js的具体源代码内容;

{"version": 3,"sources": ["webpack:///./index.js"],"names": ["App", "props", "handleClick", "bind", "fun", "console", "log", "myConsole", "Component", "document", "getElementById", "module", "hot", "accept"],"mappings": ";;;;;;;;;;;;;;AAAA;;;;AACA;;AAEA;;AACA;;AACA;;;;;;;;;;;;IAEOA,G;;;AACL,eAAaC,KAAb,EAAoB;AAAA;;AAAA,0GACbA,KADa;;AAEnB,UAAKC,WAAL,GAAmB,MAAKA,WAAL,CAAiBC,IAAjB,OAAnB;AAFmB;AAGnB;;;;kCACc;AACd,UAAIC,MAAM,SAANA,GAAM;AAAA,eAAMC,QAAQC,GAAR,CAAY,KAAZ,CAAN;AAAA,OAAV;AACAF;AACA;;;6BACQ;AACT,aAAQ;AAAA;AAAA,UAAK,SAASG,iBAAd;AAAA;AAAA,OAAR;AACA;;;;EAXiBC,gB;;AAcnB,sBACC,8BAAC,GAAD,OADD,EAECC,SAASC,cAAT,CAAwB,MAAxB,CAFD;;AAKA,IAAIC,IAAJ,EAAgB;AACfA,SAAOC,GAAP,CAAWC,MAAX,CAAkB,kCAAlB,EAAkC,YAAY;AAC7C;AACA,GAFD;AAGA,C","file": "app.9b34b0372e9214ced0b7.hot-update.js","sourcesContent": ["import React, {Component} from 'react';\r\nimport {render} from 'react-dom';\r\n\r\nimport './style.css'\r\nimport './index.css'\t\r\nimport myConsole from './console.js'\r\n\r\n class App extends Component {\r\n \tconstructor (props) {\r\n \t\tsuper(props);\r\n \t\tthis.handleClick = this.handleClick.bind(this);\r\n \t}\r\n \thandleClick () {\r\n \t\tlet fun = () => console.log('123');\r\n \t\tfun();\r\n \t}\r\n\trender () {\r\n\t\treturn  <div onClick={myConsole}>Hello World, I am changed again again...</div>\r\n\t}\r\n}\r\n\r\nrender(\r\n\t<App/>,\r\n\tdocument.getElementById('root')\r\n)\r\n\r\nif (module.hot) {\r\n\tmodule.hot.accept('./console.js', function () {\r\n\t\tmyConsole();\r\n\t})\r\n}"],"sourceRoot": ""
}

启用HMR模式的情况下,更新前后控制台的输出分别如如图4和图5所示,对比entrypoint可以看到,当有代码更新时,webpack的代码更新机制依赖于[hash].hot-update.json及[hash].hot-update.js。

在这里插入图片描述

图4 - HMR模式下,代码更新前控制台输出

在这里插入图片描述

图5 - HMR模式下,代码更新后控制台输出


http://chatgpt.dhexx.cn/article/WQCDuAxH.shtml

相关文章

webpack中的HMR(热更新)原理剖析

简介 Hot Module Replacement&#xff08;以下简称 HMR&#xff09;是 webpack 发展至今引入的最令人兴奋的特性之一 &#xff0c;当你对代码进行修改并保存后&#xff0c;webpack 将对代码重新打包&#xff0c;并将新的模块发送到浏览器端&#xff0c;浏览器通过新的模块替换…

HMR(热替换)

HMR 即模块热替换&#xff08;hot module replacement&#xff09;的简称&#xff0c;它可以在应用运行的时候&#xff0c;不需要刷新页面&#xff0c;就可以直接替换、增删模块。webpack 可以通过配置 webpack.HotModuleReplacementPlugin 插件来开启全局的 HMR 能力&#xff…

面试官:说说Webpack的热更新是如何做到的?原理是什么?

一、是什么 HMR全称 Hot Module Replacement&#xff0c;可以理解为模块热替换&#xff0c;指在应用程序运行过程中&#xff0c;替换、添加、删除模块&#xff0c;而无需重新刷新整个应用 例如&#xff0c;我们在应用运行过程中修改了某个模块&#xff0c;通过自动刷新会导致整…

Webpack 热更新HMR 原理全解析

一、什么是 HMR HMR 全称 Hot Module Replacement&#xff0c;中文语境通常翻译为模块热更新&#xff0c;它能够在保持页面状态的情况下动态替换资源模块&#xff0c;提供丝滑顺畅的 Web 页面开发体验。 HMR 最初由 Webpack 设计实现&#xff0c;至今已几乎成为现代工程化工具…

curl.perform() pycurl.error: (23, 'Failed writing body (0 != 59)')

在使用python3.7编码时&#xff0c;引入pycurl模块和StringIO模块后&#xff0c;容易引起上述错误 导入StringIO模块的解决方案&#xff1a; 只有在python2中才能导入StringIO模块&#xff0c;直接 from StringIO import StringIO 即可 但是python3&#xff0c;STringIO和…

关于python的url处理

基本环境&#xff1a; python2.7 1 完整的url语法格式&#xff1a; 协议://用户名密码:子域名.域名.顶级域名:端口号/目录/文件名.文件后缀?参数值#标识 2 urlparse模块对url的处理方法 urlparse模块对url的主要处理方法有&#xff1a;urljoin/urlsplit/urlunsplit/urlpar…

windows10+python3.7使用pip安装pycurl失败

使用pip install pycurl安装pycurl失败&#xff1a; python setup.py egg_info did not run successfully. 可以单独下载pycurl依赖文件然后安装 sArchived: Python Extension Packages for Windows - Christoph Gohlke (uci.edu) 选择Python对应版本的文件进行下载&#xff0…

Pycurl介绍

pycurl — A Python interface to the cURL library Pycurl包是一个libcurl的Python接口.pycurl已经成功的在Python2.2到Python2.5版编译测试过了. Libcurl是一个支持FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 和 LDAP的客户端URL传输库.libcurl也支持HTTPS认证,H…

[windows]python 安装pycurl

问题描述 pip install pycurl 报错 手动安装 下载地址&#xff1a;https://www.lfd.uci.edu/~gohlke/pythonlibs/ 页面搜索&#xff1a; pycurl 下载对应版本的whl文件&#xff0c;我是windows环境 python3.8 所以下载pycurl-7.45.1-cp38-cp38-win32.whl 安装&#xff1a;…

Python实用模块之pycurl

软硬件环境 ubuntu 19.04 64bitanaconda3 with python 3.7.3pycurl 7.43.0.2 简介 CURL是一个基于URL进行数据传输的命令行工具&#xff0c;使用C语言编写&#xff0c;支持http&#xff0c;https&#xff0c;ftp&#xff0c;telnet&#xff0c;file&#xff0c;ldap等常见网络传…

ipcs -a

消息队列、共享内存、信号量

ipcc

IPCCX装完后连接不了LDAP,怎么解决&#xff1f;&#xff1f; 装完了IPCCX, 靠&#xff0c; 直接给我来歌60秒关机&#xff0c; 后面还有LDAP连接问题&#xff0c; 我的IPCCX server 可以ping通ccm server, 为什么LDAP会挂呢&#xff1f;&#xff1f;&#xff1f; 请问我现在要怎…

ipcs报错:kernel not configured for shared memory、semaphore、message queues [解决方法]

前言 今天在复习linux进程间通信的shared memory 共享内存时&#xff0c;在PC端的VMare Workstation虚拟机的Ubuntu上测试我写的shared_memory_CREAT.c 和shared_memory_CONSUME.c 时正常在PC端运行&#xff0c;就想着把程序用交叉编译器编译成arm格式放到linux开发板上运行试…

ipcs

&#xfeff;&#xfeff; linux命令-ipcs 格式&#xff1a;ipcs [-asmq] [-tclup] ipcs [-smq] -i id ipcs -h 功能描述&#xff1a;ipcs命令用来显示系统存在的ipc&#xff08;进程间通信&#xff09;相关信息。 参数&#xff1a;-i 显示指定id的ip…

IPC方案

近期了解了不少网络摄像头相关知识&#xff0c;主要功能组成如下&#xff1a; WIFI&#xff0c;USB接口或者SDIO接口实现 RJ45 本地TF存储 IR CUT&#xff0c;滤光片切换 移动侦测&#xff0c;人体感应 夜视功能&#xff0c;依靠红外灯 云台控制&#xff0c;PWM控制Moto&#…

Linux15 --- 信号量、ipcs

1、IPC机制&#xff1a; 进程间通信&#xff08;管道、信号量、共享内存、消息队列、套接字&#xff09; 2、信号量&#xff1a; 可以类比于红绿灯&#xff0c;对于路口这个共享的通行权&#xff0c;谁得到红绿灯的通行信号&#xff0c;才可以得到路口的通行权&#xff0c;没…

Linux ipcs命令与ipcrm命令的用法详解

转载地址&#xff1a;http://www.jb51.net/article/40805.htm linux/uinx上提供关于一些进程间通信方式的信息&#xff0c;包括共享内存&#xff0c;消息队列&#xff0c;信号 ipcs用法 ipcs -a 是默认的输出信息 打印出当前系统中所有的进程间通信方式的信息 ipcs -m 打印出…

ipcs命令详解——共享内存、消息队列、信号量定位利器

多进程间通信常用的技术手段包括共享内存、消息队列、信号量等等&#xff0c;Linux系统下自带的ipcs命令是一个极好的工具&#xff0c;可以帮助我们查看当前系统下以上三项的使用情况&#xff0c;从而利于定位多进程通信中出现的通信问题。目前也有一些帖子介绍ipcs命令的使用方…

(1)IPC简介

Unix/Linux IPC简介 简述1. 消息传递演变过程2. 同步形式演变 进程、线程与信息共享IPC对象的持续性名字空间fork、exec和exit对IPC对象的影响总结参考资料 简述 IPC是进程间通信(interprocess communication)的简称。用来描述运行在一个操作系统之上的不同进程间各种消息传递…

Linux——信号量(定义、示例、信号量接口、ipcs命令)

目录 1、信号量 2、信号量举例 3、信号量的接口 4、通过控制进程来完成打印机操作 5、ipcs命令 1、信号量 &#xff08;1&#xff09;定义:​​​​​​ ​信号量是一个特殊的变量&#xff0c;一般取正数值。它的值代表允许访问的资源数目&#xff0c;获取资源时&#xff…