在umi中实现纯前端版本检测更新提示

背景

在中后台SPA项目里,在发布了新版本后,由于浏览器缓存、不同路由文件懒加载等的机制存在,此时用户浏览器所加载到的文件,并非是最新的代码,甚至可能是新旧版本代码杂糅在一块,从而可能引发一些问题。如何在项目部署之后,提醒用户有版本更新,并且引导用户刷新页面,以获取最新的版本文件?

实现思路

  • 在项目打包时,生成一个随机数在version.json文件里,然后去轮询这个文件,如果当前随机数和上次随机数不同,则触发更新提示
  • 关于轮询,有 setTimeoutsetIntervalwindow.requestAnimationFrameAPI可以实现,在浏览器标签页面进入休眠时(例如电脑edge浏览器)、浏览器App进入后台时(手机),定时器可能会停止运行或代码运行变慢😱,而且由于消息队列的原因,setTimeoutsetInterval可能并不能保证真正执行代码的时间
  • 不过我们的场景对于定时器并没有太高的要求,因此选择setInterval即可
  • 代码参考:纯前端实现检测版本发布更新提示 - 掘金 (juejin.cn)

实现webpack插件

/config/generate-version-plugin.ts

ts 复制代码
import fs from "fs";
import path from "path";
import { type webpack } from "umi";

const NAME = "update-version";

function generateFile(path: string, content: string) {
  fs.writeFileSync(path, content);
}

class GenerateVersionPlugin {
  options: { versionFileName?: string; keyName?: string } = {};
  version: string = "";
  constructor(options: { versionFileName?: string; keyName?: string }) {
    this.options = {
      // json 版本文件名称
      versionFileName: "update_version.json",
      // json key 值
      keyName: "UPDATE_VERSION",
      ...options,
    };
    this.version = `${Date.now()}`;
  }
  apply(compiler: webpack.Compiler) {
    compiler.hooks.beforeRun.tap(NAME, () => {
      console.log(process.env.NODE_ENV);
      console.log("before run");

      // 生成的版本 json 文件建议放置在 public 文件夹下
      const filePath = path.resolve("public", this.options.versionFileName as string);
      console.log(filePath);

      // 生成文件
      generateFile(filePath, `{"${this.options.keyName}": "${this.version}"}`);
    });

    compiler.hooks.done.tap(NAME, () => {
      console.log("done ...");
    });
  }
}

export default GenerateVersionPlugin;

注册webpack插件

/config/config.ts

ts 复制代码
import proxy from "api/proxy";
import { defineConfig } from "umi";
import GenerateVersionPlugin from "./generate-version-plugin";

export default defineConfig({
  chainWebpack(memo, { env }) {
    if (env === "production") {
      memo.plugin("GenerateVersionPlugin").use(new GenerateVersionPlugin({}));
    }
  }
});

轮询version.json文件

ts 复制代码
/**
 * 读取到更新json文件版本内容
 */
function fetchUpdateVersionFile() {
  return new Promise((resolve, reject) => {
    // 注意:文件请求路径 /update_version.json,是相对于在 public 文件下的 index.html 的位置而言的,/update_version.json 代表 update_version.json 文件与 index.html 文件夹同目录。
    fetch(`/update_version.json?_v=${Date.now()}`)
      .then((res) => {
        // console.log(res.body);
        return res.body;
      })
      .then((body) => {
        const reader = body?.getReader();
        reader
          ?.read()
          .then((val: any) => {
            let str = "";
            for (let i = 0; i < val.value.length; i++) {
              str += String.fromCharCode(val.value[i]);
            }
            return JSON.parse(str);
          })
          .then((json) => {
            resolve(json);
          })
          .catch((err) => {
            reject(err);
          });
      })
      .catch((err) => {
        reject(err);
      });
  });
}

打开更新提示

ts 复制代码
import { Modal } from "antd";

function to(promise: Promise<any>) {
  return promise
    .then((data) => [null, data])
    .catch((err) => {
      return [err, undefined];
    });
}

let currentVersion = "";
let timer: NodeJS.Timeout | null = null;

/**
 * 打开更新提示
 */
export function openUpdateVersionNotify() {
  fetchUpdateVersionFile().then(
    (res) => {
      console.log("版本号:", res);
      timer = setInterval(async () => {
        const [err, res] = await to(fetchUpdateVersionFile());
        if (err) return;
        console.log(res);
        if (!currentVersion) {
          currentVersion = res["UPDATE_VERSION"];
        }
        if (res["UPDATE_VERSION"] !== currentVersion) {
          console.log("版本更新了。。。");
          clearInterval(timer as NodeJS.Timeout);
          Modal.confirm({
            title: "温馨提示",
            content: (
              <div>
                检测到新的系统更新,包含了性能提升与全新功能,为了您获得更好的体验,建议立即更新。
              </div>
            ),
            okText: "立即更新",
            cancelText: "稍后更新",
            onOk() {
              location.reload();
            },
          });
        }
      }, 1000 * 60);
    },
    (err) => {
      console.log("更新版本:", err);
    },
  );
}

在app.ts入口文件执行

ini 复制代码
if (process.env.NODE_ENV === "production") {
  openUpdateVersionNotify();
}
相关推荐
AI多Agent协作实战派1 分钟前
AI多Agent协作系统实战(二十三):Agent读HEARTBEAT.md不读AGENTS.md——openclaw的文件加载之谜
前端·数据库·人工智能·uni-app
2601_955760071 小时前
如何用 Claude Opus 5 API 批量扩展长尾关键词和文章选题
前端·python·搜索引擎
KaMeidebaby1 小时前
卡梅德生物技术快报|bli亲和力检测gst:告别批量跑胶:BLI实时酶切监测技术加速GST融合蛋白下游流程优化
前端·网络·数据库·人工智能·算法
触底反弹2 小时前
🚀 删了数据刷新又回来?3 组件 × 4 回调 × 3 坑讲透 React 父子通信
前端·javascript·react.js
耳东小鹿3 小时前
对象常用方法
前端
程序员海军3 小时前
一个 AI 应用开发程序员的一天,都在屏幕前忙些什么?
前端·后端·程序员
程序员黑豆3 小时前
鸿蒙应用开发:Stack堆叠组件实战——实现微信消息角标效果
前端·harmonyos
夏幻灵3 小时前
浏览器原理深度解析:从输入 URL 到页面展现的全流程剖析
前端
闲猫4 小时前
Agent工程实践:从WorkFlow到多Agent协作的落地笔记
java·服务器·前端
带娃的IT创业者4 小时前
重新定义前端构建速度:深度解析 SWC 如何用 Rust 颠覆 JavaScript 工具链
前端·javascript·rust·前端构建·swc