前端SPA应用如何做版本部署更新通知?

1,405 阅读3分钟

在当前SPA(单页)应用流行的大背景下,由于单页应用的特殊性(只有一个index.html页面),浏览器对index.html缓存会导致前端版本部署更新,用户无法及时得到更新通知,功能无法实时生效。

主要方案

1、服务端主动通知:

部署更新后后端主动通知到用户,这种方案常用的就是websocket,需要保持长链接,且需要后端共同维护版本信息。针对用户少的情况可用,一旦用户量上来,就会非常消耗服务器资源。只适合用户量少的情况。所以理想跟现实还是存在一定差距的。

2.用户侧主动获取版本信息

在一定时机,由前端触发获取版本信息,这样可以消除websocket的缺点,但是实时性稍微差一点,不过也还能接受。该方案主要是时机的选择上要合适,常用时机:

  • 定时器轮训方案

    一定间隔时间 ,请求版本信息,对比上一次缓存的版本信息,如果有变更则通知用户更新。该方案存在问题就是定时间隔时间设置不好控制,间隔时间长了,时效性就变差;间隔时间短了,请求太频繁给服务器压力变大,尤其是用户量达到一定量级,一样会存在资源消耗问题

  • 在路由切换时检测版本更新

这是目前比较好的一种方案,在用户切换页面时检测,不会影响用户当前页面的操作。时效性来说也是OK的,毕竟切换页面肯定是有操作的目的性,此时检测版本是比较合适的。目前我们正式采用这种方案。

该方案的实现就是构建时把版本信息打入index.html,然后路由切换时获取该页面文本,然后做比较。上代码

构建层面:这里采用的webpack打包构建

const webpackConfig={
 plugins:[
  new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './public/index.html',
      chunks: ['main'], // entry中的app入口才会被打包
      favicon: path.resolve('./src/common/assert/images/favicon.ico'),
      meta: {
        version: dayjs().format('YYYY-MM-DD HH:mm:ss'),
      },
      env: {
        env: process.env.NODE_ENV,
      },
    }),
 
 ]
}

versionCheck.ts

import axios from 'axios';

import { message } from 'common/extends/message';

function findVersionHash(doc: HTML) {
  const version = doc.querySelector('meta[name="version"]');
  if (version) {
    return version.getAttribute('content');
  }
  return '';
}

function updateHandler() {
  axios
    .get(location.origin, {
      headers: {
        'Cache-Control': 'max-age=0', //不缓存
      },
    })
    .then((res) => {
      const parser = new DOMParser();
      const doc = parser.parseFromString(res.data, 'text/html');
      if (findVersionHash(doc) !== findVersionHash(document)) {
        message
          .confirm('检测到系统可能已更新,需要刷新才能正常访问页面,是否立刻刷新?')
          .then((isOk) => {
            isOk && location.reload();
          });
      }
    });
}

export function onDeployUpdate() {
  window.addEventListener('hashchange', updateHandler); //hash 路由
  window.addEventListener('popstate', updateHandler); //history 路由
}

main.ts


import { onDeployUpdate } from 'common/utils/version';

onDeployUpdate();
  • 接口层面加入前端版本信息

这个方案是写文章此刻根据我司现在的部署模式想到的,可不可行还得验证。不过整体要求会比较高,不一定适合每个人,不过分享出来给大家参考参考。 因为目前我司前端是基于docker镜像部署的,所以操作空间可操作空间也就比较大,不过对于部署与请求架构要求比较高。

  1. 首先需要部署前端静态资源的nginx同时做接口转发工作,这样能在构建流程与版本信息变更自动化实现
  2. 镜像构建时在nginx_conf往代理接口的配置请求头加入版本号版,然后前端每次接口请求从请求头拿出来版本号跟本地存储的版本做比较

nginx_config.sh

image.png