前言
老板:新的需求不是上线了嘛,怎么用户看到的还是老的页面呀
窝囊废:让用户刷新一下页面,或者清一下缓存
老板:那我得告诉用户,刷新一下页面,或者清一下缓存,才能看到新的页面呀,感觉用户体验不好啊,不能直接刷新页面嘛?
窝囊废:可以解决(OS:一点改的必要没有,用户全是大聪明)
产品介绍
c端需要经常进行一些文案调整,一些老版的文字字眼可能会导致一些舆论问题,所以就需要更新之后刷新页面,让用户看到新的页面。
思考问题为什么产生
项目是基于vue的spa应用,通过nginx代理静态资源,配置了index.html协商缓存,js、css等静态文件Cache-Control
,按正常前端重新部署后, 用户重新
访问系统,已经是最新的页面。
但是绝大部份用户都是访问页面之后一直停留在此页面,这时候前端部署后,用户就无法看到新的页面,需要用户刷新页面。
产生问题
- 如果后端接口有更新,前端重新部署后,用户访问老的页面,可能会导致接口报错。
- 如果前端部署后,用户访问老的页面,可能无法看到新的页面,需要用户刷新页面,用户体验不好。
- 出现线上bug,修复完后,用户依旧访问老的页面,仍会遇到bug。
解决方案
- 前后端配合解决
- WebSocket
- SSE(Server-Send-Event)
- 纯前端方案 以下示例均以vite+vue3为例;
- 轮询html Etag/Last-Modified
在App.vue中添加如下代码
1const oldHtmlEtag = ref();
2const timer = ref();
3const getHtmlEtag = async () => {
4 const { protocol, host } = window.location;
5 const res = await fetch(`${protocol}//${host}`, {
6 headers: {
7 "Cache-Control": "no-cache",
8 },
9 });
10 return res.headers.get("Etag");
11};
12
13 oldHtmlEtag.value = await getHtmlEtag();
14 clearInterval(timer.value);
15 timer.value = setInterval(async () => {
16 const newHtmlEtag = await getHtmlEtag();
17 console.log("---new---", newHtmlEtag);
18 if (newHtmlEtag !== oldHtmlEtag.value) {
19 Modal.destroyAll();
20 Modal.confirm({
21 title: "检测到新版本,是否更新?",
22 content: "新版本内容:",
23 okText: "更新",
24 cancelText: "取消",
25 onOk: () => {
26 window.location.reload();
27 },
28 });
29 }
30 }, 30000);
- versionData.json
自定义plugin,项目根目录创建/plugins/vitePluginCheckVersion.ts
1import path from "path";
2import fs from "fs";
3export function checkVersion(version: string) {
4 return {
5 name: "vite-plugin-check-version",
6 buildStart() {
7 const now = new Date().getTime();
8 const version = {
9 version: now,
10 };
11 const versionPath = path.join(__dirname, "../public/versionData.json");
12 fs.writeFileSync(versionPath, JSON.stringify(version), "utf8", (err) => {
13 if (err) {
14 console.log("写入失败");
15 } else {
16 console.log("写入成功");
17 }
18 });
19 },
20 };
21}
在vite.config.ts中引入插件
1import { checkVersion } from "./plugins/vitePluginCheckVersion";
2plugins: [
3 vue(),
4 checkVersion(),
5]
在App.vue中添加如下代码
1const timer = ref()
2const checkUpdate = async () => {
3 let res = await fetch('/versionData.json', {
4 headers: {
5 'Cache-Control': 'no-cache',
6 },
7 }).then((r) => r.json())
8 if (!localStorage.getItem('demo_version')) {
9 localStorage.setItem('demo_version', res.version)
10 } else {
11 if (res.version !== localStorage.getItem('demo_version')) {
12 localStorage.setItem('demo_version', res.version)
13 Modal.confirm({
14 title: '检测到新版本,是否更新?',
15 content: '新版本内容:' + res.content,
16 okText: '更新',
17 cancelText: '取消',
18 onOk: () => {
19 window.location.reload()
20 },
21 })
22 }
23 }
24}
25
26onMounted(()=>{
27 clearInterval(timer.value)
28 timer.value = setInterval(async () => {
29 checkUpdate()
30 }, 30000)
31})
Use
1
2import { defineConfig } from 'vite'
3import vue from '@vitejs/plugin-vue'
4import { webUpdateNotice } from '@plugin-web-update-notification/vite'
5
6
7export default defineConfig({
8 plugins: [
9 vue(),
10 webUpdateNotice({
11 logVersion: true,
12 }),
13 ]
14})
个人笔记记录 2021 ~ 2025