前端页面更新检测实战:一次关于「用户不刷新」的需求拉扯战

这是我在真实项目里为「用户不刷新看不到新页面」做的一次完整治理,从方案推演、和产品拉扯,到最终落地 无感知版本更新提示 的全过程。所有代码以 Vite + Vue3 为例。

1. 🎬 开场:深夜工位的"灵魂三问"

产品(10:02 PM)

新的页面不是刚上线吗,怎么用户还是看到老版本?

我(10:03 PM)

让用户刷新一下页面,或者清下缓存就好了。

产品(10:04 PM)

我要告诉用户"请刷新",这体验太差了。能不能自动刷新?

我(OS)

......(明明就是 SPA + Nginx 静态资源,这事儿不复杂,但真想做好,也不简单。)


2. 🧭 背景与约束

  • 架构:Vue3 + Vite 打包,Nginx 托管静态资源
  • 缓存index.html协商缓存 (Etag/Last-Modified),*.js/*.css强缓存Cache-Control + 文件名带 hash)
  • 真实场景 :用户长时间停留 在页面不动;前端上线后,不清空缓存刷新就永远看不到新版本
  • 风险
    1. 后端接口变更,老页面还在调用旧协议,线上报错
    2. 修 bug 后,用户还在用旧代码,无法获得修复
    3. 提示文案错误,必须尽快全量生效

于是,我们需要一个前端可控、无后端强依赖、能温和推动用户刷新的方案。


3. 🕰 时间线:从"拍脑袋"到"可灰度的工程方案"

Day 1:先跟产品吵一架(然后一起找共识)

  • 产品 :我就要"用户完全无感知,像原生 App 一样自动更新"。

  • :Web 不是 App。强制刷新会打断用户交互,会丢失表单输入数据。

  • 折中方案

    • 若当前无风险操作:弹一个"新版本可用"提示 → 用户确认后刷新
    • 若风险更新(协议变更、紧急修复) :在下一次用户点击可控元素时触发刷新(给用户一个可预期的时机)
    • 对超长驻留页 :默默在后台定时检测版本 ,可选静默预加载资源

4. ⚔️ 解决方案

方案一:轮询检测 index.html 的 Etag

最简单粗暴的办法:

  • 定时向服务器请求一次首页 HTML。
  • 如果发现 Etag 变化,提示用户刷新。

App.vue中添加如下代码

js 复制代码
<script setup>
import { ref, onMounted } from 'vue';
import { Modal } from 'ant-design-vue';

const oldHtmlEtag = ref();
const timer = ref();

const getHtmlEtag = async () => {
  const { protocol, host } = window.location;
  const res = await fetch(`${protocol}//${host}`, {
    headers: { "Cache-Control": "no-cache" },
  });
  return res.headers.get("Etag");
};

onMounted(async () => {
  oldHtmlEtag.value = await getHtmlEtag();
  clearInterval(timer.value);
  timer.value = setInterval(async () => {
    const newHtmlEtag = await getHtmlEtag();
    if (newHtmlEtag !== oldHtmlEtag.value) {
      Modal.confirm({
        title: "检测到新版本,是否更新?",
        okText: "更新",
        cancelText: "取消",
        onOk: () => window.location.reload(),
      });
    }
  }, 30000); // 每 30 秒检测一次
});
</script>

产品(犹豫) :30 秒一次会不会太频繁?
:不然用户等半天都没感知到更新。


方案二:版本文件 versionData.json

为了减少请求压力,我们想到生成一个 versionData.json 文件,记录版本号。

自定义 Vite 插件plugins/vitePluginCheckVersion.ts

js 复制代码
import path from "path";
import fs from "fs";

export function checkVersion() {
  return {
    name: "vite-plugin-check-version",
    buildStart() {
      const now = new Date().getTime();
      const version = { version: now };
      const versionPath = path.join(__dirname, "../public/versionData.json");
      fs.writeFileSync(versionPath, JSON.stringify(version), "utf8");
    },
  };
}

并在 vite.config.ts 中引入:

js 复制代码
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { checkVersion } from "./plugins/vitePluginCheckVersion";

export default defineConfig({
  plugins: [
    vue(),
    checkVersion(),
  ]
});

App.vue中定时请求该文件,发现版本号更新则提示刷新页面

js 复制代码
const timer = ref(null)
const checkUpdate = async () => {
  let res = await fetch('/versionData.json', {
    headers: {
      'Cache-Control': 'no-cache',
    },
  }).then((r) => r.json())
  if (!localStorage.getItem('demo_version')) {
    localStorage.setItem('demo_version', res.version)
  } else {
    if (res.version !== localStorage.getItem('demo_version')) {
      localStorage.setItem('demo_version', res.version)
      Modal.confirm({
        title: '检测到新版本,是否更新?',
        content: '新版本内容:' + res.content,
        okText: '更新',
        cancelText: '取消',
        onOk: () => {
          window.location.reload()
        },
      })
    }
  }
}

onMounted(()=>{
  clearInterval(timer.value)
  timer.value = setInterval(async () => {
   checkUpdate()
  }, 30000)
})

方案三:现成库 plugin-web-update-notification

懒人方案,直接用第三方库:

js 复制代码
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { webUpdateNotice } from '@plugin-web-update-notification/vite';

export default defineConfig({
  plugins: [
    vue(),
    webUpdateNotice({
      logVersion: true,  // 控制台打印版本
      checkInterval: 30 * 1000 // 自定义检查间隔
    }),
  ]
})
  • 优点:方便,省时。
  • 缺点:但定制化不足,UI 可能不符合项目风格。

5. 🏁 最终选择:方案二 + 提示弹窗

经过与产品多轮拉扯,决定:

  • 采用 versionData.json ,性能好,可控性强。
  • 提示弹窗由我们自定义,用户可选择立即刷新或稍后。

上线当天,产品亲自测试:

  • 打开旧页面,静静等待。
  • 弹窗出现------ "检测到新版本,是否更新?"
  • 点了"更新",页面瞬间重载,加载了最新的内容。

产品(满意地点点头) :说道小王,可以加你个wx吗?

看似一个简单的"刷新页面",背后却藏着缓存策略、用户体验、性能优化的多重博弈。

相关推荐
狂炫冰美式5 分钟前
当硅基神明撞上人类的“叹息之墙”:距离证明哥德巴赫猜想,AI还有多远?
前端·算法·架构
毕设源码-邱学长19 分钟前
【开题答辩全过程】以 基于Vue的爱心公益募捐平台的设计与实现为例,包含答辩的问题和答案
前端·javascript·vue.js
IT_陈寒25 分钟前
Redis实战精要:5种高频使用场景与性能优化全解析|得物技术
前端·人工智能·后端
许泽宇的技术分享1 小时前
Agent Framework:性能优化
性能优化
Hilaku1 小时前
那个把代码写得亲妈都不认的同事,最后被劝退了🤷‍♂️
前端·javascript·代码规范
南囝coding1 小时前
Node.js 原生功能狂飙,15 个热门 npm 包要失业了
前端·后端
Dragon Wu1 小时前
TanStack Query(React Query) 常用api及操作总结
前端·javascript·前端框架
火柴就是我1 小时前
canvas.rotate(rotation); 到底是往哪个方向转动
前端
光影少年1 小时前
前端算法新手如何刷算法?
前端·算法
梦想是准点下班1 小时前
【vue3】 + 【vite】 + 【vite-plugin-obfuscator】混淆打包 => 放弃了,样式会丢
前端·vue.js