前言
在开发环境中,Vite 能够实时推送代码更新到浏览器,这样我们不需要手动刷新页面,能更高效地开发。但在生产环境中,如何实现类似的自动更新功能呢?当我们更新了代码并重新部署时,浏览器如何知道这些更新并提醒用户刷新页面?
在生产环境中,我们通常会将打包后的 HTML 和 JS 文件部署到静态资源服务器上。用户在访问网页时,浏览器会加载这些文件,并在页面中执行 JS 代码。每次我们更新代码并重新打包后,生成的 JS 文件通常会有不同的指纹(比如 chunk-ae17d.js
),但浏览器并不会自动感知这些变化。
解决方案
为了让浏览器知道有新版本的代码可用,我们可以在页面中设置一个定时器,定期检查页面的 HTML 内容是否发生变化。如果检测到变化,我们就可以提醒用户刷新页面。
实现步骤
- 定期检查页面:通过定时请求当前页面,并获取页面中的 JS 文件列表。
- 比较文件列表:与上次请求的文件列表进行比较,如果有变化,则认为代码已更新。
- 通知用户:如果检测到更新,提醒用户刷新页面以获取最新内容。
示例代码
let lastSrcs: string[]; // 存储上一次获取的 JS 文件地址
// 正则表达式用于从 HTML 中提取 script 标签的 src 属性
const scriptRegex = /<script.*src=["'](?<src>[^"']+)/gm;
// 提取当前页面中所有的 JS 文件地址
async function extractNewScripts(): Promise<string[]> {
// 添加时间戳以防止缓存
const html = await fetch("/?_timestamp=" + Date.now()).then(res =>
res.text()
);
// 重置正则表达式
scriptRegex.lastIndex = 0;
let result: string[] = [];
let match;
// 从 HTML 中提取 JS 文件地址
while ((match = scriptRegex.exec(html))) {
result.push(match.groups.src);
}
return result;
}
// 检查页面是否需要更新
async function needUpdate(): Promise<boolean> {
const newScripts = await extractNewScripts();
if (!lastSrcs) {
lastSrcs = newScripts;
return false;
}
if (lastSrcs.length !== newScripts.length) {
return true;
}
for (let i = 0; i < lastSrcs.length; i++) {
if (lastSrcs[i] !== newScripts[i]) {
return true;
}
}
lastSrcs = newScripts;
return false;
}
// 每隔 2 秒检查一次是否需要更新
const duration = 2000;
function autoRefresh() {
setTimeout(async () => {
const willUpdate = await needUpdate();
if (willUpdate) {
alert("页面已更新,请刷新");
location.reload();
}
autoRefresh();
}, duration);
}
// 启动自动检查和刷新功能
autoRefresh();