在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();
}
相关推荐
无我Code37 分钟前
全套开源:一款云端服务+本地设备计算的文生图应用
前端·人工智能·后端
用户69371750013841 小时前
实测可用|小米 MiMo 百万亿 Token 免费领,开发者速冲
前端·后端·ai编程
前端小万1 小时前
令人头痛的前端环境
前端·前端工程化
明月_清风1 小时前
Nginx 模块机制深度解析:从核心原理到生产实践
前端·nginx
APIshop2 小时前
1688 跨境寻源通详情接口深度解析:从接入到实战
前端·网络·chrome
爱上好庆祝2 小时前
学习js的第四天
前端·css·学习·html·css3·js
d111111111d2 小时前
UAER问题+修复小bug
前端·javascript·笔记·stm32·单片机·嵌入式硬件·学习
kyriewen112 小时前
Next.js:让你的React应用从“裸奔”到“穿衣服”
开发语言·前端·javascript·react.js·设计模式·ecmascript
MXN_小南学前端2 小时前
基于 Vue3 + ECharts 的数据大屏实例(提供gitHub仓库地址)
前端·javascript·echarts