纯前端实现监控版本更新

1,344 阅读2分钟

背景

因为我的项目是用vite打包的,vite又是基于rollup的打包,rollup不像webpack有contentHash可以实现增量构建,而是每次打包,所有文件的hash都会更新,这样就会导致浏览器缓存的资源失效:

当用户已经打开页面,此时,重新部署代码; 会导致index html未更新, 而远程资讯已经替换,当用户切换路由时,因为按需加载的原因, 会访问旧的资源,导致404,控制台报错MIME类型错误, 此时加载的js资源content-type类型为text/html.

webpack有cententHash, vite没有这个概念,因此vite并不支持增量更新

解决思路

1、注入版本信息: 通过vite构建时,注入一个版本信息的version.json文件,json内容version: 版本号(这里会用时间戳代替)

2、定义全局常量: 通过vite提供的 define定义一个全局常量 __APP_VERSION__, __APP_VERSION__verson.json共用一个时间戳

vite对define的解释: 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。

3、监控版本信息: 在合适的时机,请求version.json,对比全局常量APP_VERSION,二者不一致即版本已更新

代码

  • 自定义vite插件:

    // vite-version-plugin.ts文件import fs from 'fs';
    import path from 'path';
    type Version = {
      version: number | string;
    };
    interface Config {
      publicDir: string;
    }
    ​
    export default ({ version }: Version) => {
      let config: Config = { publicDir: '' };
    ​
      return {
        name: 'version-plugin', // 必须的,将会在 warning 和 error 中显示
        configResolved(resolvedConfig: Config) {
          // 存储最终解析的配置
          config = resolvedConfig;
        },
    ​
        buildStart() {
          // 生成版本信息文件路径
          const file = config.publicDir + path.sep + 'version.json';
    ​
          // 编译时间作为版本信息
          const content = JSON.stringify({ version });
          writeVersion(file, content);
        }
      };
    };
    ​
    /**
     * 写入文件
     * @param fileName
     * @param version
     */
    function writeVersion(fileName: string, version: string | NodeJS.ArrayBufferView) {
      fs.writeFile(fileName, version, (err) => {
        if (err) throw err;
      });
    }
    ​
    
  • vite.config.js中引入插件,并define__APP_VERSION__,这样在打包的时候__APP_VERSION__version.json中的值是保持一致.

    ...
    import versionPlugin from './build/vite-version-plugin';
    ​
    const timeVersion = new Date().getTime();
    ​
    ...
    return defineConfig({
      define: {
          // 定义全局版本信息
          __APP_VERSION__: JSON.stringify(timeVersion)
      },
      plugins: [
        versionPlugin({
            version: timeVersion
         })
      ],
      ...
    })
    ​
    
  • 版本监控,检查__APP_VERSION__version.josn是否一致,不一致表示服务器资源已更新.

    import axios from 'axios';
    ​
    export async function versionCheck() {
      console.log('versionCheck');
    ​
      if (import.meta.env.MODE === 'development') return;
      const res = await axios.get('version.json');
      if (__APP_VERSION__ !== res.data.version) {
        // 这里看个人需求
        window.location.reload();
      }
    }
    ​
    

    版本监控方法,需在按需加载触发之前调用, 如路由的前置钩子、触发异步组件的点击事件,要在加载资源报错之前,确定版本是否已更新.