关键词:静态资源更新、页面版本更新
这个话题非常的有意思,问题的答案是比较开发的,这里仅代表作者本人的个人经验来做回答。 当然也可以自行去搜集掘金上的大佬们的博文。
首先第一个问题
用户在没有页面刷新的情况下, 如何去感知前端静态资源已经发生了更新?
首先要做静态资源版本管理。 这个版本直接给到 html 模板即可, 其他 link 打包的资源还是以哈希 code 作为文件名称后缀。
就类似于这样子的
lua
xxx.1.0.0.html --> vender.hash_1.js、 vender.hash_2.js、 vender.hash_3.js、vender.hash_1.css
xxx.1.0.1.html --> vender.hash_a.js、 vender.hash_b.js、 vender.hash_c.js、vender.hash_d.css
如何主动推送给客户端
这个实现方式就非常的多了,我这里建议让服务端来做处理
因为我们前端静态资源打包之后, 大多数会上传到云存储服务器上, 或者甚至是 服务器本地 也行。 这个时候, 后端给一个定时任务, 比如 1 分钟去执行一次, 看看是否有新的 html 版本的内容生成。 如果有新的 html 版本内容生成, 且当前用户访问的还是旧版本, 那么直接发一个服务端信息推送即可(SSE 允许服务器推送数据到浏览器)。
实现也非常简单:
下面是一个用 Node.js
和 Server-Sent Events (SSE)
实现的简单示例。这个例子包括了一个用于服务端推送事件的 HTTP 服务器,以及一个简单的静态 HTML 版本检查机制:
首先,我们可以使用 Express.js
框架进行服务端编码:
javascript
const express = require("express");
const fs = require("fs");
const path = require("path");
const app = express();
const PORT = 3000;
// 存储连接的客户端
const clients = [];
// 假设静态资源在 'public' 目录下,'index.html' 变动作为版本更新的代理
const STATIC_DIR = path.join(__dirname, "public");
const HTML_FILE = path.join(STATIC_DIR, "index.html");
let currentHtmlVersion = fs.readFileSync(HTML_FILE, "utf-8");
// 中间件提供静态文件
app.use(express.static(STATIC_DIR));
// SSE端点,用于发送消息到客户端
app.get("/events", (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive", // 保持连接
});
// 添加此客户端到数组
clients.push(res);
// 当客户端关闭连接时移除监听
req.on("close", () => {
clients.splice(clients.indexOf(res), 1);
});
});
// 路由返回当前HTML版本
app.get("/current-version", (req, res) => {
res.send(currentHtmlVersion);
});
// 启动服务器
const server = app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
checkForUpdatesPeriodically();
});
// 定时任务,每分钟检查`index.html`是否更新
function checkForUpdatesPeriodically() {
setInterval(() => {
let newHtmlVersion = fs.readFileSync(HTML_FILE, "utf-8");
if (newHtmlVersion !== currentHtmlVersion) {
currentHtmlVersion = newHtmlVersion;
notifyAllClients();
}
}, 60000); // 检查周期:1分钟
}
// 通知所有连接的客户端
function notifyAllClients() {
clients.forEach((client) => client.write(`data: update\n\n`));
}
这段代码创建了一个简单的服务器,该服务器每一分钟读取一次index.html
的内容,并检查是否有更新。如果发现更新,它会通过 SSE 发送消息给所有连接的客户端,通知它们页面已更新。
接下来,客户端需要监听来自服务器的事件,并相应地采取行动:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Page Title</title>
</head>
<body>
<h1>Content of the Page</h1>
<script>
// 创建SSE连接到服务器的/events路径
const evtSource = new EventSource("/events");
evtSource.onmessage = function (event) {
// 服务器发送更新通知时,接收事件
if (event.data === "update") {
alert("新版本页面已可用,即将为您刷新!");
window.location.reload();
}
};
</script>
</body>
</html>
这样做成本是最低的, 甚至可以说是一劳永逸。 前端是没有任何负债, 没有任何性能问题。
那是否还有别的处理方式呢? 当然是有的。
- WebSockets:
通过 WebSocket 连接,服务器可以实时地向客户端发送消息,包括静态资源更新的通知。收到消息后,客户端可以采取相应的措施,比如显示一个提示信息让用户选择是否重新加载页面。
- Service Workers(推荐):
Service workers 位于浏览器和网络之间,可以控制页面的资源缓存。它们也可用于检测资源更新,当检测到静态资源更新时,可以通过推送通知或在网站上显示更新提示。
- 轮询:
客户端用 JavaScript 定时发送 HTTP 请求到服务器,查询版本信息。如果检测到新版本,可以提醒用户或自动刷新资源。
在绝大多数情况下,使用 Service Workers 可能是最稳妥的做法,因为它不仅提供了资源缓存和管理的能力,而且也可以在后台做资源更新的检查,即使用户没有开启网页也能实现通知和更新的功能。
社区上有很多相关文章, 他们的处理办法基本上也逃不开以上几种方式
比如这篇高赞文档: juejin.cn/post/718545.... 点赞量有 1.8k, 实现的方式是前端轮询。
这篇文章也不错: juejin.cn/post/733025.... 实现方式是通过 websocket.