webpack HMR

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

{
	h: "fe94d54f9adaaa2831b2",	// new hash, h: hash
	c: {						// c: chunk, updated chunck
		app: 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.js

webpackHotUpdate("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模式下,代码更新后控制台输出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neil-

你们的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值