前端版本更新技术方案

背景

公司的移动端和PC端,分别是angular和vue库开发的项目,每次在打包发布新版本后,用户都需要手动刷新页面获取最新代码或样式,否则发布的最新的js\css\image等资源文件得不到更新,为解决此问题对两个项目做了以下改造;

技术方案说明

  • 项目在打包时利用插件生成一个时间戳作为版本信息,打入到一个json文件里

  • 前端路由拦截器里监听到路由变化时,请求这个json文件,和本地的localStorage里的版本信息做对比:

    • 如果本地没有缓存:直接做自动刷新浏览器处理,同时把json文件里的版本信息存到缓存里

    • 如果本地有缓存:对比是否有差异,无差异不做处理;有差异则调用浏览器的location.reload()做自动刷新处理,同时将服务器的版本信息存到localStorage里,再有路由变化时则不会再次刷新了,直到下次新版本发布重新走步骤2

代码实现

PC端

首先编写一个webpack plugin :VersionFilePlugin.js,主要逻辑为生成一个version.json在项目打包配置output.path的static文件夹下

const fs = require('fs');
const path = require('path');

/**
 * 生成版本信息
 */
class VersionFilePlugin {
  constructor(options) {
    this.options = options || {};
  }

  apply(compiler) {
    compiler.plugin('done', (stat) => {
      //方案:作为json文件打到dist里,然后通过网络请求进行查找version.json文件
      const version = Date.now(); // 获取当前时间戳
      const versionData = { version };
      const versionFilePath = path.join(compiler.options.output.path, 'static/version.json');
      // 写入version.json文件
      fs.writeFileSync(versionFilePath, JSON.stringify(versionData));
    });
  }
}

module.exports = VersionFilePlugin;

在webpack.prod.conf里引入这个插件(uat和prod的打包都是使用的这个文件,开发环境的webpack.dev.conf不需要引入此插件功能)

const VersionFilePlugin = require('../plugins/VersionFilePlugin');

plugins:[
    ...省略其他代码
    new VersionFilePlugin()  在所有plugin的最后引入这个VersionFilePlugin
]

到此步为止,运行pc的npm run build:shuat 或者 npm run build:shprod可以看到打包出的dist文件夹下已有一个version.json文件了

img.png

img.png

接下来在api里请求这个json文件,/api/version.js

import request from '../utils/request';

//获取版本信息
export function getVersion() {
  return request.MainService({
    url: '/pc/static/version.json',
    method: 'get',
  });
}

在app.vue里的全局监听路由变化进行调用上面的api

watch: {
  $route(val, old) {
    //获取版本信息
    getVersion().then(data => {
      if(data && data.version){
        versionExamine(data.version);
      }
    })
  }
}

versionExamine方法是util里的工具方法,用于对比本地缓存里的versionData,此工具方法移动和pc可以共用

//版本检查:根据本地缓存的版本信息和服务器的版本信息进行对比
export function versionExamine(timestamp){
  let storage_version = localStorage.getItem('versionData');
  if(storage_version){//本地有版本信息则进行对比
    if(Number(timestamp) !== Number(storage_version)){
      localStorage.setItem('versionData',timestamp);
      console.log('强制刷新页面~~');
      location.reload();
    }
  }else{//没有的话先存下版本信息,再进行刷新页面
    localStorage.setItem('versionData',timestamp);
    console.log('强制刷新页面~~');
    location.reload();
  }
}

最后,如有版本更新,在pc的页面进行路由跳转时会自动进行刷新浏览器的当前tab页,同时可看到【强制刷新页面~~】的log
img.png

移动端

由于移动端的的angular项目很老,是angular5的版本(目前官方已更新16),直接使用PC的同一个webpack插件的方式不适用于这个版本,所以做了以下修改:

这个生成version.json的插件不再是一个webpack形式,而是直接使用node里的fs模块生成一个文件出来,在执行打包时把它当做一个命令顺序执行:

version-plugin.js

const fs= require('fs');
const version = Date.now(); // 获取当前时间戳
const versionData = { version };
// 写入version.json文件
console.log('versionData', versionData);
fs.writeFileSync('./dist/assets/version.json', JSON.stringify(versionData));

现在package.json里增加一个命令,然后修改uat的打包命令:

"version-plugin": "node ./version-plugin.js",

uat修改前的打包命令:

"produat": "npm run git-plugin --max_old_space_size=16384 && node --max_old_space_size=16384 ./node_modules/@angular/cli/bin/ng build  --dev --aot --build-optimizer  --environment=uat"

在原本的命令后面加上 && npm run version-plugin,修改后:

"produat": "npm run git-plugin --max_old_space_size=16384 && node --max_old_space_size=16384 ./node_modules/@angular/cli/bin/ng build  --dev --aot --build-optimizer  --environment=uat && npm run version-plugin"

然后运行流水线,可看到运行记录:
img.png

img.png

再查看移动端的部署日志可看到,已生成成功:
img.png

和pc一样,接下来在app.component.ts的路由里调用api,执行util里的versionExamine方法,进行对比处理:
img.png

如有版本更新会自动进行刷新浏览器的当前tab页,可同样看到【强制刷新页面~~】的log
img.png

声明

此功能上线之后,只有更新到这个方案的客户端才具备路由跳转进行版本更新的功能

此功能不具备解决线上有关逻辑性的报障,可解决一些因为版本落后导致的js/css/image等资源更新的情况