背景
前端项目开发过程中热更新的机制大家都知道,不知道你在开发的时候是否做了这方面的配置。
相信接触最多的就是 webpack 的热更新,文件保存后页面自动刷新,或者 css 自动更新,页面的样式在不刷新页面的情况下就会更新。
还有就是模块热替换。
热更新机制很好玩,能提升不少开发效率,但是只是处于会用的阶段不是我们的目的,我们应该适当的深入学习下,看看他背后的原理,一个是否思考过,一个是否能自己实现。
热更新原理
咱们这里主要说下怎样自己实现一个热更新,也就是文件更改了会自动刷新页面,可以同步 pc 和 移动端,css 更改了可以不刷新页面就应用最新的 css。
其实热更新的原理并不复杂,或者说很简单。
咱们一步一步的分析下。
本文不是要告诉你一些 api如何使用,而是利用架构的思维去分析和解决问题。
【分析】
- 文件内容变更了,浏览器是怎么知道的呢?
- css 文件内容变更了,没有刷新页面 怎么加载最新的内容呢?
只要解决了上面两个问题,我们就算是完成了。因为剩下得就是编码了,这都好说。
【结果】
文件变更了,我怎样通知浏览器?
- 浏览器和服务器保持着连接。 服务器有什么事儿直接通过当前的链接告诉浏览器就可以了。
连接肯定是长连接,不然怎么实时通信。
保持长连接有哪些方法呢? 轮询?eventSorce? 都不够好。
有么有更好的方案呢?那就是 - websocket
浏览器和服务器先建立好链接,服务器就可以直接通知到客户端了。这个时候无论是 pc 上还是手机上都可以随时根据需要刷新或者加载资源。
- css 更新,css 本身是可以通过 dom 去操作的。浏览器只要知道是 css更新了,直接重新加载当前的 css 文件就可以了。
架构思维
咱们在重新捋捋这个架构。
- 服务器和浏览器通过 websocket 建立链接。
- 服务器和浏览器规定好消息的规则,是刷新页面还是更新 css。
基本架构有了,其他的就是编码实现了。
服务端使用 node 创建一个 ws 服务。
浏览器使用 websocket 创建一个链接和服务器进行链接。
双方通过对应的 api 进行数据的操作。
代码实现
本文只是讲解下思路,并没有实现文件的监听,文件监听后面会介绍。咱暂时先确定好两个消息规则:
浏览器收到 命令为:htmlFileChange ,此时浏览器刷新;
浏览器收到命令为:cssFileChange,此时不刷新页面,自动加载 css 文件;
具体代码如下:
服务端:
//web-socket.js 创建 ws 服务
var ws = require("nodejs-websocket");//需要安装这个包
module.exports = function(){
return function () {
console.log("重度前端提醒,开始建立连接...")
var sessions = [];//存放每一个链接对象
var server = ws.createServer(function (conn) {
sessions.push(conn);//将新的链接对象存放在数组中
conn.on("text", function (str) {
console.log("收到的信息为:" + str)
sessions.forEach(item=>{
item.sendText(str) //所有客户端都发送消息
});
});
conn.on("close", function (code, reason) {
console.log("关闭连接")
});
conn.on("error", function (code, reason) {
console.log("异常关闭")
});
}).listen(6152)
console.log("WebSocket建立完毕")
}
}
//server.js http 服务代码
let http = require('http');
let fs = require('fs');
let webSocket = require('./node/web-socket');
const BASEROOT = process.cwd();//获得当前的执行路径
//读取 index.html内容
let getPageHtml = function () {
let data = fs.readFileSync(BASEROOT+'/html/index.html');
return data.toString();
}
//读取 index.css内容
let getPageCss = function () {
let data = fs.readFileSync(BASEROOT + '/html/index.css');
return data.toString();
}
//node 端 开启 ws 服务
webSocket()();
http.createServer(function (req, res) {//创建 http 服务
let body = '',url = req.url;
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
//路由简单处理 根据不同路径输出不同内容给浏览器
if(url.indexOf('/index.css')>-1){
res.write(getPageCss());
}else{
res.write(getPageHtml());
}
res.end();
});
}).listen(6151);
console.log('重度前端提醒...... server start');
页面截图
客户端
//index.html 布局代码省略
const nick = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'aa', 'cc'];
let index = 0;
// Create WebSocket connection.
const socket = new WebSocket('ws://10.70.69.191:6152');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send(navigator.userAgent);
});
// 监听服务器推送的消息
socket.addEventListener('message', function (event) {
if (index > nick.length) {
index = 0;//只是为了每次输出不同的昵称,没实际意义
}
var ele = document.createElement('div');
ele.innerHTML = nick[index] + ':' + event.data;
if (event.data === 'htmlFileChange') {
//html 文件更新了 刷新当前页面
location.reload();
}
if (event.data === 'cssFileChange') {
//css 文件更新了 刷新当前页面
reloadCss();
}
document.getElementById('content').append(ele);
index += 1;
});
//重新加载 css
function reloadCss() {
var cssUrl = [],
links = document.getElementsByTagName('link'),
len = links.length;
for (var i = 0; i < len; i++) {
var url = links[i].href;
document.getElementsByTagName('head')[0].appendChild(getLinkNode(url)); //创建新的 css 标签
document.getElementsByTagName('head')[0].removeChild(links[i]); //移除原有 css
}
console.log(document.getElementsByTagName('head')[0])
function getLinkNode(cssUrl) {
var node = document.createElement('link');
node.href = cssUrl;
node.rel = 'stylesheet';
return node;
}
}
document.getElementById('btn1').onclick = function () {
socket.send(document.getElementById('message').value);
document.getElementById('message').value = '';
}
index.css 内容
input {
outline: none;
}
#content {
height: 400px;
width: 400px;
border: solid 1px #ccc;
color: red;
}
代码倒是次要的。解决问题的思路才重要。有了解决问题的架构思维,代码实现都好说。
写到这里咱们还能顺便实现一个群聊。
本质就是服务器和浏览器怎样实时通信,解决了这个问题,其他的都是小事儿。
这个技术实现还是比较简单的。
另外对模块热更新和 websocket 原理有兴趣的可以研究下,后面可能也会介绍。
总结
本文主要介绍
简易版热更新的原理;
热更新实现思路和代码实现;
架构思维:简单的带出架构思维的作用;
希望本文对你有用。
原创不易、请多鼓励
自家观点、欢迎打脸
代码示例下载
https://github.com/bigerfe/ho...
作者:微信公众号 - 重度前端 主笔:八门
欢迎关注 重度前端-每周5原创全栈干货+每周三深度技术文章
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。