前言
在日常的前端开发中,相信很多小伙伴们应该对前端热更新又熟悉又陌生。一开始听说前端还有热更新,我表示疑惑 前端还有热更新? 一般来说热更新是手游这种比较庞大的APP等等需要热更新,可以通过加载补丁包去更新 也叫做热替换等等。那么在前端开发中热更新指的又是什么呢?
热更新(Hot Module Replacement
)原理
在开发过程中 当我们文件修改了 webpack会对代码进行一个新的编译,并把托管在服务器上的项目进行一个刷新或者是局部模块重载,从而达到了自动刷新的效果,提高了开发者的效率。而在服务端和浏览器之间,什么时候需要刷新?需要全局liveReload 还是hmr 局部去动态刷新? 首先我们先捋清楚服务器和浏览器之间的关系和交互。
通过这个图我们来简单分析下两者的交互流程
- 通过webpack启一个默认为8080的服务,并且启一个
WebSocket
服务 端口为3000 然后通过客户端去打开这个8080的地址 那这个服务器上是托管了我们的项目。 - 那客户端也就是我们托管在服务器上的项目需要去连接 刚刚启动的
WebSocket
服务 这样两个就建立起了连接。 - 两者之间建立起连接了,那么问题就解决了一半,如果文件修改了 那么这个更改会被
webpack
的watch
这个插件捕获到,继而发送给客户端,让客户端去进行下一步的刷新操作
简单手写热更新
-
第一步我们首先要用webpack初始化一个项目, 因为框架一般已经帮你载入热更新啦。那么初始化项目要是有同学不懂的话可以去查阅webpack 文档哦 这里就不多做解说 温馨提示道路 webpack.docschina.org/concepts/
-
简单初始化了项目 项目目录大概是这样 我们可以先把
webpack.config.js
里面的devServer
相关配置关闭
devServer: {
open: false,
hot: false, // 关闭热更新
liveReload: false, // 关闭自动刷新
webSocketServer: false,
},
复制代码
- 通过webpack 自己去启一个默认服务 和一个WebSocket服务 就我们上述说的8080 和 3000的端口
// server 服务
const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('../webpack.config.js');
const devServerOptions = { ...webpackConfig.devServer };
// webServer
const webServer = new WebpackDevServer(devServerOptions, compiler);
const runServer = async () => {
console.log('Starting server...');
await webServer.start(); // 需要手动start() 启动
};
runServer();
复制代码
// websocket 服务
const connections = []; //存放链接对象
var ws = require("nodejs-websocket")
const server = ws.createServer(function (conn) {
connections.push(conn);
console.log("New connection")
conn.on("text", function (str) {
console.log("Received "+str)
conn.sendText(str.toUpperCase()+"!!!")
})
conn.on("error", function (code) { // 链接错误 就关闭当前链接
try {
socket.close();
} catch (error) {}
});
conn.on("close", function (code, reason) {
const nowIndex = connections.findIndex(item => item === conn); // 链接断开了 把当前失效链接删除
connections.splice(nowIndex, 1);
console.log("Connection closed")
})
}).listen(3000);
// webpack 监听
const watching = compiler.watch({
aggregateTimeout: 300,
poll: undefined
}, (err, stats) => { // [Stats Object](#stats-object)
if(!err) {
connections.forEach((item) => {
item.sendText('[HMR] Module Update');
})
}
});
复制代码
- 通过
webpack
成功启动了服务,那么下一步我们需要去连接这个websocket
服务 那么我们可以在public下的index.html中去写JS层逻辑
const address = "ws://localhost:3000"; // websocket 服务 注意非http/ https 是ws协议
var socket = new WebSocket(address);
socket.onopen = function () {
console.log("[HMR] Live reload enabled."); // 建立连接
};
复制代码
注意 在这里我们可以执行下通过node 去执行刚刚我们前面完成的两个步骤
在这里我们可以看到8080这个端口了 然后打开这个页面可以看到控制台打印了 这说明两者之间已经建立起了连接
- webpack 的 watch 我们在每个连接更新都重新发送了text 那么在项目里我们收到了text 我们就可以去进行下一步的操作了
const address = "ws://localhost:3000"; // websocket 服务 注意非http/ https 是ws协议
var socket = new WebSocket(address);
socket.onopen = function () {
console.log("[HMR] Live reload enabled."); // 建立连接
};
socket.onmessage = function (msg) {
console.log("msg", msg); // 接收到msg
window.location.reload(); // 全局刷新
};
复制代码
这时候我们修改文件内容的话,返回控制台可以看到打印的msg 那么就可以实现刷新了
总结 通过建立连接以后我们大致可以知道了热更新的操作流程了 那么这个是属于热更新
吗? 其实严格意义上来说刚刚上面那一步算是热刷新
在我们监听到文件修改了 直接去手动全局刷新,虽然说是提高了开发效率但是在性能和消耗上还不是最优。 是属于实现了liveReload 全局刷新,那么真正的hot 该如何实现 如何去局部刷新,局部载入新模块替换旧模块呢? 移步下一篇文章哦 通过解析webpack-dev-server
源码去分析hmr
思想 (文章正在肝的过程中....)写的不足希望大佬补充
在服务器和客户端建立长链接的时候为什么选择使用websocket
两者之间保持长链接的方式其实不仅仅是只有websocket方式
轮询
轮询也算是服务器和客户端之间保持长连接的一种方式 但是!太消耗性能啦! >< 五星不推荐
EventSource
EventSource
是基于http协议 只是简单的单项通信 实现方式比 Websocket
更简单一点,并且可以自动重连接等等,而WebSocket
要借助第三方库比如socket.io
可以实现重连。 但是EventSource
只能发送文本,而websocket支持发送二进制数据等等 单项通信适合实现单向推送播报等等 综合考虑 Websocket
更适合去建立两者之间的链接。
对这两者的区别和优缺点感兴趣的可以康康 www.jianshu.com/p/958eba34a… 比较详细哦 (>< 搬运)