作者:微信小助手
发布时间:2021-04-17T11:57:43
模块热替换( 一般的刷新我们分两种: 可以看到相比于第一种,热更新对于我们的开发体验以及开发效率都具有重大的意义 具体我们如何在 设置 再设置一下 当我们改变我们项目的文件的时候,比如我修改 更改前: 更改后: 浏览器会去请求两个文件 接下来我们看看这两个文件: 那么问题来了,我修改了文件,浏览器是怎么知道要更新的呢? 热更新使用到了 上面通过 new Websocket 创建一个客户端与服务端通信的实例,并通过 这里大概解释下,为什么是 接下来让我们进一步的讨论关于热更新的原理 几个重要的概念(这里有一个大致的概念就好,后面会把它们串起来): 文件经过 下面流程图中的 1、2、A、B阶段 文件经过 下面流程图的 1、2、3、4、5 阶段 参考 19 | webpack中的热更新及原理分析 我们还看回上图,其中启动阶段图中的 1、2、A、B阶段就不讲解了,主要看热更新阶段主要讲 3、4 和 5 阶段 在开始接下开的阅读前,我们再回到最初的问题上我本地修改了文件,浏览器是怎么知道要更新的呢? 通过上面的流程图,其实我们可以猜测,本地实际上启动了一个 所以我们聚焦以下几点: 以下的源码解析分别对应的版本是: 这个工作主要是在 看 启动服务结束之后就通过 什么是热更新
hot module replacement 或 HMR
)是 webpack
提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新
window.location.reload()
。
WDS (Webpack-dev-server)
的模块热替换,只需要局部刷新页面上发生变化的模块,同时可以保留当前的页面状态,比如复选框的选中状态、输入框的输入等。
HMR
作为一个 Webpack
内置的功能,可以通过 HotModuleReplacementPlugin
或 --hot
开启。webpack
中使用这个功能呢?热更新的使用以及简单分析
如何使用热更新
npm install webpack webpack-dev-server --save-dev
HotModuleReplacementPlugin
,HotModuleReplacementPlugin
是 webpack
是自带的plugins: {
HotModuleReplacementPlugin: new webpack.HotModuleReplacementPlugin()
}devServer
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
hot: true, // 重点关注
historyApiFallback: true,
compress: true
}
hot
为
true
,代表开启热更新
两个重要的文件
Vue
的一个 方法:clickMe() {
console.log('我是 Gopal,欢迎关注「前端杂货铺」');
}clickMe() {
console.log('我是 Gopal,欢迎关注「前端杂货铺」,一起学习成长吧');
}
JSON
文件,
h
代表本次新生成的
Hash
值为
0c256052432b51ed32c8
——本次输出的
Hash
值会被作为下次热更新的标识。
c
表示当前要热更新的文件对应的是哪个模块,可以让
webpack
知道它要更新哪个模块
{
"h": "0c256052432b51ed32c8",
"c": {
"201": true
}
}
js
文件,就是本次修改的代码,重新编译打包后的,大致是下面这个样子(已删减一些并格式化过,这里看不懂没关系的,就记住是返回要更新的模块就好了),
webpackHotUpdate
方法就是用来更新模块的,
201
对应的是哪个模块(我们称它为模块标识),其他的就是要更新的模块的内容了
webpackHotUpdate(201, {
"./src/views/moveTransfer/list/index.vue?vue&type=script&lang=js&": function (
module,
exports,
__webpack_require__
) {
"use strict";
var _Object$defineProperty = __webpack_require__(
/*! @babel/runtime-corejs3/core-js-stable/object/define-property */ "./node_modules/@babel/runtime-corejs3/core-js-stable/object/define-property.js"
);
_Object$defineProperty(exports, "__esModule", {
value: true,
});
exports.default = void 0;
var _default = {
data: function data() {
return {};
},
computed: {},
methods: {
clickMe: function clickMe() {
console.log("我是 Gopal,欢迎关注「前端杂货铺」,一起学习成长吧");
},
},
};
exports.default = _default;
},
});了解一下 Websocket
Websocket
,这里不会细讲 Websocket
,可以看下阮一峰老师的 WebSocket 教程,下面是一个 简单的例子// 执行上面语句之后,客户端就会与服务器进行连接。
var ws = new WebSocket("wss://echo.websocket.org");
// 实例对象的 onopen 属性,用于指定连接成功后的回调函数
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
// 实例对象的 onmessage 属性,用于指定收到服务器数据后的回调函数。可以接受二进制数据,blob 对象或者 Arraybuffer 对象
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
// 实例对象的 onclose 属性,用于指定连接关闭后的回调函数。
ws.onclose = function(evt) {
console.log("Connection closed.");
}; onmessage
属性,接受指定服务器返回的数据,并进行相应的处理。Websocket
?因为 Websocket
是一种双向协议,它最大的特点就是 服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送信息。这是 HTTP
不具备的,热更新实际上就是服务器端的更新通知到客户端,所以选择了 Websocket
热更新原理
热更新的过程
Webpack-complier
:
webpack
的编译器,将
JavaScript
编译成
bundle
(就是最终的输出文件)
HMR Server
:将热更新的文件输出给
HMR Runtime
Bunble Server
:提供文件在浏览器的访问,也就是我们平时能够正常通过
localhost
访问我们本地网站的原因
HMR Runtime
:开启了热更新的话,在打包阶段会被注入到浏览器中的
bundle.js
,这样
bundle.js
就可以跟服务器建立连接,通常是使用
websocket
,当收到服务器的更新指令的时候,就去更新文件的变化
bundle.js
:构建输出的文件
启动阶段
Webpack-complier
编译好后传输给 Bundle Server
,Bundle Server
可以让浏览器访问到我们打包出来的文件文件热更新阶段
Webpack-complier
编译好后传输给 HMR Server
,HMR Server
知道哪个资源(模块)发生了改变,并通知 HMR Runtime
有哪些变化(也就是上面我们看到的两个请求),HMR Runtime
就会更新我们的代码,这样我们浏览器就会更新并且不需要刷新
深入——源码阅读
HMR Server
服务,而且在启动 Bundle Server
的时候已经往我们的 bundle.js
中注入了 HMR Runtime
(主要用来启动 Websocket
,接受 HMR Server
发来的变更)
Webpack
如何启动了 HMR Server
HMR Runtime
接受到变更之后,如何生效的
5.24.3
4.0.0-beta.0
4.1.0
启动 HMR Server
webpack-dev-server
中完成的lib/Server.js
setupApp
方法,下面的 express 服务实际上对应的是 Bundle Server
setupApp() {
// Init express server
// eslint-disable-next-line new-cap
// 初始化 express 服务
// 使用 express 框架启动本地 server,让浏览器可以请求本地的静态资源。
this.app = new express();
}createSocketServer
创建 websocket
服务listen(port, hostname, fn) {
this.hostname = hostname;
return (
findPort(port || this.options.port)
.then((port) => {
this.port = port;
return this.server.listen(port, hostname, (err) => {
if (this.options.hot || this.options.liveReload) {