前言
前端项目构建时默认使用了 chunkhash 进行了文件缓存控制,但是项目的 index.html 文件会存在被浏览器缓存的情况,部分用户没有关闭网页的习惯,此时进行内容更新或问题修复,用户还是会使用到旧版本,影响用户体验,也有可能会因为浏览器使用旧的 index.html 文件,向服务器端请求旧资源,hash 不一致,出现报错、白屏等。
方法一:打包时生成版本文件
思路是在每次打包获取当前时间,赋值给全局变量,同时生成一个版本文件。由于全局变量会被缓存,而文件会被 chunkhash,所以第二次打包时全局变量的版本和文件中的版本会不一致,在页面变化时比对两个版本号,如果不一致则刷新页面。同时为了保证刷新后不会请求到旧的 index.html,需要设置 nginx 为不缓存状态。
配置 nginx
在 nginx.conf 文件中配置 expires,-1 表示永不缓存。
server {
location / {
expires -1;
}
}
生成版本号
1. 在 src 目录创建 versionUpdate.ts,编写 vite 打包插件。
使用 fs 模块, build 时在 pubilc 目录生成 version.json 文件,记录当前的打包时间。
import fs from "fs";
export default (options: any) => ({
name: "version-update",
buildStart() {
fs.writeFile("public/version.json", `{ "version": ${options.version} }\n`, (err) => {
if (err) throw err;
});
},
});
2. 在 vite.config.ts 中加载 versionUpdate.ts
获取当前打包时间,将参数分别传递给全局变量和 versionUpdatePlugin 插件。
import versionUpdatePlugin from "./versionUpdate";
// 获取当前时间
const now = new Date().getTime();
export default () =>
defineConfig({
define: {
APP_VERSION: now,
},
plugins: [
// 版本更新
versionUpdatePlugin({
version: now,
}),
});
比对版本号
在路由守卫或者其他时机进行版本号的比对,如果处于构建环境并且版本号不一致则刷新页面。
// 版本号比对
const checkVersion = () => {
return new Promise<void>((resolve) => {
Axios.get("/version.json", {
headers: { "Cache-Control": "no-cache" },
})
// 请求到 json 文件内容, 并且禁止缓存
.then((res: any) => {
APP_VERSION !== res?.data?.version && resolve();
});
});
};
import.meta.env.VITE_ENV === "production" &&
checkVersion().then(() => {
// 版本不一致,执行更新逻辑
});
方法二:使用 plugin-web-update-notification 插件
原理是以 git commit hash (也支持 svn revision number、package.json version、build timestamp、custom) 为版本号,打包时将版本号写入 json 文件。客户端轮询服务器上的版本号(浏览器窗口的 visibilitychange、focus 事件辅助),和本地作比较,如果不相同则通知用户刷新页面。支持 vite、umi、webpack。
以 vite 项目为例
1. 安装插件
pnpm add @plugin-web-update-notification/vite -D
2. 在 vite.config.ts 中使用
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { webUpdateNotice } from '@plugin-web-update-notification/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
logVersion: true,
}),
]
})
3. 自定义通知栏文本
// vite.config.ts
export default defineConfig({
plugins: [
vue(),
webUpdateNotice({
notificationProps: {
title: '标题',
description: 'System update, please refresh the page',
buttonText: '刷新',
dismissButtonText: '忽略'
},
}),
]
})
总结
进行版本更新的方式有很多,本质上是通过对比版本号对用户进行通知,根据具体业务形式和资源,可以选择前端事件通知、轮询、websocket等。