监控前端代码版本迭代实现页面自动刷新

监控前端代码版本迭代实现页面自动刷新

背景:

当前端版本迭代较为频繁的时候,使用webpack对项目进行打包,虽然我们对js和css文件使用了chunkhash进行了文件缓存控制,但是项目的index.html文件在版本频繁迭代更新时,会存在被浏览器缓存的情况。在发版后,用户不强制刷新页面,浏览器会使用缓存的index.html文件,从而导致向服务器端请求了上个版本chunkhash的js和css文件,最终页面404(上个版本chunkhash的js和css在版本更新时已替换删除了)。

output: {
  path: config.build.assetsRoot,
  filename: utils.assetsPath('js/[name].[chunkhash].js'),
  chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}

解决思路:

  1. 服务器端发版,上一个版本的代码不删掉;
  2. 在每次打包生产代码时,在static下生产一个version.json的版本信息文件,在前端页面实时请求服务器端的version.json中的版本号和浏览器本地缓存的version.json进行对比,从而监控版本迭代更新,实现页面自动更新,获取新的index.html文件(前提是服务器端对index.html进行不缓存配置)。

思路1:缺点是会随着频繁发版,服务器端前端项目文件会越来越多,浪费空间,同时,旧页面的接口涉及到接口后端同学已经废弃了,引起报错;

所以现在主要以思路2进行处理:

  1. 配置环境
// config/dev.env.js
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  VERSION: '""'
})

// config/prod.env.js
'use strict'
module.exports = {
  NODE_ENV: '"production"', // 区分开发/生产环境
  VERSION: '"v' + new Date().getTime() + '"' // 版本格式
}

  1. 自定义版本信息生成插件: 如何编写一个插件?
'use strict';

var FStream = require('fs');

/**
 * 版本信息生成插件
 * @author guoqian.xu
 * @param options
 * @constructor
 */
function VersionPlugin(options) {
  this.options = options || {};
  !this.options.versionDirectory && (this.options.versionDirectory = 'static');
}

// apply方法是必须要有的,因为当我们使用一个插件时(new somePlugins({})),webpack会去寻找插件的apply方法执行
VersionPlugin.prototype.apply = function (compiler) {
  var self = this;
  compiler.plugin('compile', function (params) {
    // 生成版本信息文件路径
    // this.options.context:项目的绝对路径
    var dir_path = this.options.context + '/' + self.options.versionDirectory;
    var version_file = dir_path + '/version.json';
    var content = '{"version":' + self.options.env.VERSION + '}';
    FStream.exists(dir_path, function (exist) {
      if (exist) {
        writeVersion(self, version_file, content);
        return;
      }
      FStream.mkdir(dir_path, function (err) {
        if (err) throw err;
        console.log('\n创建目录[' + dir_path + ']成功');
        writeVersion(self, version_file, content);
      });
    });
  });
  // 编译器对'所有任务已经完成'这个事件的监听
  compiler.plugin('done', function (stats) {
    console.log('应用编译完成!');
  });
};

const writeVersion = (self, versionFile, content) => {
  console.log('\n当前版本号:' + self.options.env.VERSION);
  console.log('开始写入版本信息...');
  // 写入文件
  FStream.writeFile(versionFile, content, function (err) {
    if (err) throw err;
    console.log('版本信息写入成功!');
  });
};

module.exports = VersionPlugin;
  1. 加载插件
// webpack.prod.config.js
// 版本信息生成
const VersionPlugin = require('./version-plugin');
...
const webpackConfig = merge(baseWebpackConfig, {
  ...
  plugins: [
    // 版本信息生成
    new VersionPlugin({
      path: config.build.assetsRoot,
      env: env,
      versionDirectory: 'static'
    }),
    ...
  ]
  ...
});

  1. 选择你需要的位置进行监控(路由钩子、特定页面、接口调用拦截等)
// 版本监控
async versionCheck() {
  if (NODE_ENV === 'development') return;
  const response = await this.$ajax.get(`../static/version.json`);
  if (VERSION !== response.data.version) {
    this.$alert('发现新版本,自动更新中...', '温馨提示', {
      confirmButtonText: '我知道了',
      type: 'warning',
      closeOnClickModal: false,
      closeOnPressEscape: false,
      showClose: false,
      callback: action => {
        window.location.reload(true);
      }
    });
  }
}

这里说一下在路由钩子的检测实现:

// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import routeInterceptor from './hooks';
...

router.beforeEach(routeInterceptor);

// hooks/index.js
/* hooks 目录用于配置路由钩子 */
import routerViewChange from './routerViewChange';
...

const allHooks = [
  /* 放置全部的路由钩子 */
  routerViewChange
];
const routeInterceptor = ({ path: toPath, query: toQuery, matched }, { path: fromPath }, next) => {
  // 路由拦截
  if (matched.length === 0) {
    // 404页面
    next({ path: '/error/404' });
  } else {
    // 找到匹配当前路由的钩子
    const hookMatched = allHooks.filter(({ path: hookPath }) => {
      if (hookPath instanceof RegExp) {
        const routerPathReg = hookPath;
        return routerPathReg.test(toPath);
      }
      return hookPath === toPath;
    });
    const hookLen = hookMatched.length;
    if (hookLen) {
      let hookActived = 0;
      // 匹配到路由钩子后, 触发该路由下的全部钩子函数
      hookMatched.forEach((hook /* ,  idx */) => {
        hook.action({
          toPath,
          fromPath,
          toQuery,
          next(params) {
            // 使用计数器控制 保证全部路由钩子均执行完毕 (包括异步调用 next 函数)后, 才继续路由导航
            if (hookActived < hookLen - 1) {
              hookActived += 1;
              return;
            }
            next(params);
          }
        });
      });
    } else {
      next();
    }
  }
};

export default routeInterceptor;


// hooks/routerViewChange.js
import { ajax } from '@/utils';

const routerViewChange = {
  path: /\/\w+?\//, // 匹配所有路由
  action: async({ toPath, next }) => {
    if (NODE_ENV === 'development') return;
    const response = await ajax.get(`../static/version.json`);
    if (VERSION !== response.data.version) {
      window.confirm('发现新版本,自动更新中...');
      window.location.reload(true);
    }
    next();
  }
};

export default routerViewChange;

  1. 作者使用的是nginx,这里实现nginxindex.html的不缓存处理
location ~ .*\.(htm|html)?$ {
    add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    # 指明root,如果不在外层上指明root,需要在这里指明
    root /data/server/ui/vue-project/dist/;
}

最后产出的结果:

版本打包
产出文件
效果

在Vue3和Vite中实现版本自动刷新的方法下: 1. 配置Vite插件:在vite.config.js文件中,可以使用versionUpdatePlugin插件来实现版本自动刷新。该插件可以在每次打包生产代码时,在public目录下生成一个version.json版本信息文件,并将版本号定义为全局变量__APP_VERSION__。同时,该插件还可以监控版本迭代新,并在页面跳转时,通过请求服务器端的version.json文件和浏览器本地缓存的版本号进行对比,从而实现页面自动新。 ```javascript // vite.config.js import { defineConfig } from 'vite' import versionUpdatePlugin from './versionUpdatePlugin' export default defineConfig((config) => { const now = new Date().getTime() return { define: { __APP_VERSION__: now, // 定义全局变量__APP_VERSION__ }, plugins: [ // 使用versionUpdatePlugin插件 versionUpdatePlugin({ version: now, }), ], // 其他配置项... } }) ``` 2. 创建versionUpdatePlugin插件:在项目根目录下创建一个名为versionUpdatePlugin.js的文件,并在该文件中定义versionUpdatePlugin插件。 ```javascript // versionUpdatePlugin.js export default function versionUpdatePlugin(options) { return { name: 'version-update-plugin', apply: 'build', generateBundle(_, bundle) { const version = options.version const versionInfo = { version: version, } const versionJson = JSON.stringify(versionInfo) // 在public目录下生成version.json文件 this.emitFile({ type: 'asset', fileName: 'version.json', source: versionJson, }) // 其他操作... }, } } ``` 通过以上配置,每次打包生产代码时,会在public目录下生成一个version.json文件,并将版本号定义为全局变量__APP_VERSION__。在页面跳转时,可以通过请求服务器端的version.json文件和浏览器本地缓存的版本号进行对比,从而实现页面自动新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值