寒假赋闲在家,没啥业余爱好,闲暇之余喜欢学学好看的css效果,然每次更改完样式都需要刷新浏览器,于是去寻热加载工具,未果,自己写了一个🚀
当然,您也可以用react提供的cra等类似工具实现此需求,但是我因为就想用简单一点的方式编辑css效果,所以不喜勿喷哈
下文的网页热加载均指此需求
注:如果您是想用于生产环境,那就不用往下看,这里纯粹图一乐哈
思路
网页热加载大概分为一下步骤
-
开启一个端口实现http服务
-
server监听文件夹
-
文件夹有变动的时候通过
SSE发送给client -
client刷新重新获取页面
所以我们要用到的工具有
- Node包:
http,fs - WebAPIs:
localtion.reload(),EventSource
开始
提供http服务
首先,我们开启一个端口来提供http服务
const http = require('http');
const server = http.createServer((req, res) => {
});
server.listen(8080, () => {
console.log(`server running on http://localhost:8080/🚀...`)
});
node运行一下,这个时候服务已经开启啦http://localhost:8080/
接下来要提供静态资源的访问服务,这样我们才能看到我们的网页呀
这里我们使用fs.readFile读取文件并响应http请求
注意⚠⚠更换自己的静态资源文件夹路径
const fs = require('fs');
//别忘了改成自己监听的文件夹
fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
if (err) {
res.writeHead(404);
res.end("File Not Found");
return;
}
res.writeHead(200);
res.end(data);
});
加到我们的http服务中就是这样
这里我们默认访问根目录返回index.html,用一个函数封装一下给其他静态资源复用
const http = require("http");
const fs = require("fs");
const getStaticResource = (url, res) => {
fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
if (err) {
res.writeHead(404);
res.end("File Not Found");
return;
}
res.writeHead(200);
res.end(data);
});
};
const server = http.createServer((req, res) => {
const { headers, url, method } = req;
if (url.toLowerCase() === "/" && method.toLowerCase() === "get") {
getStaticResource("/index.html", res);
return;
}
if (method.toLowerCase() === "get") getStaticResource(url, res);
});
server.listen(8080, () => {
console.log(`server running on http://localhost:8080/🚀...`);
});
这样的话,我们在相应的文件夹下建立index.html就能在http://localhost:8080/
看到我们的网页啦
使用SSE和文件监控实现热加载
接下来就是实现热加载啦
首先我们的网页中一定要默认链接一个JS文件,里面放入我们SSE需要的浏览器端API
//sse.js
const events = new EventSource("/events"); //监听我们http服务中的/events路由
events.onmessage = (event) => {
location.reload();
};
设定好监听的路由,只需要设置收到信息的时候刷新页面就好啦
不要忘记链接到HTML中
<script src="sse.js"></script>
接下来实现我们server端的服务
首先是文件监听APIfs.watch,我们要配合这个来给浏览器端发送消息
老样子,写一个函数包装一下,并且应该是立即执行的(因为我们想打开端口就用,如果有业务逻辑则不要学我)
(function watchFile() {
fs.watch("./public", { encoding: "buffer" }, () => {
client.writeHead(200, {
"Content-Type": "text/event-stream",
"Connection": "keep-alive",
"Cache-Control": "no-cache"
});//headers标准
client.end("data: reflush\n\n");//别忘了data:和\n\n
});
})();
你可能发现,我这里多了一个client变量,不过别急我待会解释,要注意SSE的通信标准(代码中注释)
由于SSE的本质是不断向server端发送GET请求保持连接一直未中断
而我们只需要文件更改的时候发送信息给浏览器端,所以我这样安排了响应
if (url.toLowerCase() === "/events" && method.toLowerCase() === "get") {
client = res;
return;
}
每次响应我们只需要保存“指针”,以便于文件更改的时候能找到
其实这里全局变量有些不优雅,但是别计较太多
到这里我们的功能已经实现完全啦
全代码
const http = require("http");
const fs = require("fs");
let client;
const server = http.createServer((req, res) => {
const { headers, url, method } = req;
if (url.toLowerCase() === "/" && method.toLowerCase() === "get") {
getStaticResource("/index.html", res);
return;
}
if (url.toLowerCase() === "/events" && method.toLowerCase() === "get") {
client = res;
return;
}
if (method.toLowerCase() === "get") getStaticResource(url, res);
});
server.listen(8080, () => {
console.log(`server running on http://localhost:8080/🚀...`);
});
(function watchFile() {
fs.watch("./public", { encoding: "buffer" }, () => {
client.writeHead(200, {
"Content-Type": "text/event-stream",
"Connection": "keep-alive",
"Cache-Control": "no-cache"
});//headers标准
client.end("data: reflush\n\n");//别忘了data和/n/n
});
})();
const getStaticResource = (url, res) => {
fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
if (err) {
res.writeHead(404);
res.end("File Not Found");
return;
}
res.writeHead(200);
res.end(data);
});
};
打开你的代码编辑器和http://localhost:8080/
尽情享受吧!