在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();
}
相关推荐
weixin_489690021 分钟前
MicroSIP自定义web拨打协议
服务器·前端·windows
幻云201014 分钟前
Python机器学习:筑基与实践
前端·人工智能·python
web小白成长日记16 分钟前
Vue3中如何优雅实现支持多绑定变量和修饰符的双向绑定组件?姜姜好
前端·javascript·vue.js
晴天飛 雪22 分钟前
Spring Boot 接口耗时统计
前端·windows·spring boot
0思必得030 分钟前
[Web自动化] Selenium模拟用户的常见操作
前端·python·selenium·自动化
Apifox.43 分钟前
测试用例越堆越多?用 Apifox 测试套件让自动化回归更易维护
运维·前端·后端·测试工具·单元测试·自动化·测试用例
玉梅小洋1 小时前
Chrome设置链接自动跳转新标签页而不是覆盖
前端·chrome
EndingCoder1 小时前
反射和元数据:高级装饰器用法
linux·运维·前端·ubuntu·typescript
Marshmallowc1 小时前
React性能优化:useState初始值为什么要用箭头函数?深度解析Lazy Initialization与Fiber机制
前端·react.js·性能优化·前端框架·react hooks
Coder_Boy_1 小时前
基于SpringAI的在线考试系统-试卷管理模块完整优化方案
前端·人工智能·spring boot·架构·领域驱动