如何检测项目代码更新,并提示用户刷新页面

252 阅读1分钟

解决方案

在项目打包时,将index.html文件中加入打包时的时间戳,当用户第一次进入页面时,获取index.html中的时间戳并保存。然后通过轮询的方式,每隔一段时间请求一下index.html资源,并且根据请求到的内容,来获取对应的时间戳,当保存的时间戳和最新获取的时间戳相同,则表示没有更新,如果不同则表示有新代码发布,提示用户刷新页面。

一、打包时增加打包时间,在index.html中使用

// vue.config.js
module.exports = {
  chainWebpack: (config) => {
    config.plugin("html").tap(([options]) => {
      const saveTemplateParameters = options.templateParameters;
      options.templateParameters = (...args) => ({
        ...saveTemplateParameters.apply(options, args),
        BUILD_TIME: new Date().getTime(), // 增加BUILD_TIME字段
      });
      return [options];
    });
  },
};

二、index.html文件中使用BUILD_TIME字段

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
    
    <!-- 方案一:项目中需要引入配置文件,在引入地址后面加上打包时间 -->
    <script src="<%= BASE_URL %>config.js?<%= BUILD_TIME %>"></script>
    
    <script>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    
    <!-- 方案二:没有配置文件,可以将BUILD_TIME放到dom中,只要能获取到该值就行 -->
    <div id="build-time" style="display: none;"><%= BUILD_TIME %></div>
    
  </body>
</html>

三、轮询获取index.html文件,并校验时间戳是否变更

// app.vue 中,或者 main.js 中
<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      buildTime: null,
      timeId: null,
    };
  },
  mounted() {
    if (process.env.NODE_ENV === "production") {
      this.openCheckUpdate();
    }
  },
  methods: {
    // 开启更新检测
    openCheckUpdate() {
      const outerHTML = document.documentElement.outerHTML;
      this.buildTime = this.getBuildTime(outerHTML);
      this.pollingGetNewHtml();
    },
    // 轮询获取html
    pollingGetNewHtml() {
      this.timeId = setInterval(async () => {
        const { data } = await axios.get("/index.html");
        const nowBuildTime = this.getBuildTime(data);
        if (this.buildTime !== nowBuildTime) {
          this.closeCheckUpdate();
          this.notifyUpdate();
        }
      }, 2000);
    },
    // 关闭更新检测
    closeCheckUpdate() {
      if (this.timeId) {
        clearInterval(this.timeId);
        this.timeId = null;
      }
    },
    // 通知用户
    notifyUpdate() {
      console.log('请刷新页面同步最新代码'); // eslint-disable-line
      // TODO:可以弹出提示框或者直接让页面刷新等等
    },
    // 通过正则获取buildTime
    getBuildTime(outerHTML) {
      const [, buildTime] = /config\.js\?(\d+)/g.exec(outerHTML) || [];
      return buildTime;
    },
  },
  beforeDestroy() {
    this.closeCheckUpdate();
  },
};
</script>

如果是将buildTime放入dom中,只有获取buildTime方式不同

getBuildTimeOfDom(outerHTML) {
  const execArr = /<div id="build-time" style="display: none;">\d+<\/div>/g.exec(outerHTML) || [];
  return execArr[0].replace(/<[^>]+>/g,'');
},