在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();
}
相关推荐
一 乐10 分钟前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
BBB努力学习程序设计1 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计1 小时前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
冰暮流星1 小时前
css之动画
前端·css
jump6802 小时前
axios
前端
spionbo2 小时前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户4099322502122 小时前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天2 小时前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者2 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js