06月28, 2024

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

背景

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

实现思路

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

实现webpack插件

/config/generate-version-plugin.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

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文件

/**
 * 读取到更新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);
      });
  });
}

打开更新提示

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入口文件执行

app.ts

if (process.env.NODE_ENV === "production") {
  openUpdateVersionNotify();
}

本文链接:https://587v5.com/post/zai-umi-zhong-shi-xian-chun-qian-duan-ban-ben-jian-ce-geng-xin-ti-shi.html

Comments