likes
comments
collection
share

纯前端方案检测系统升级(望指教)

作者站长头像
站长
· 阅读数 16

背景:最近项目里面有接到到提示用户升级需求,因此针对此需求进行了问题调研,希望这篇文章可以给大家带来启发或者参考

1、现有技术分析

经过对网上文章的浏览发现市面上比较常用的技术方案大致分为以下几种:

1.websockt: 前端页面建立websocket连接,实时接收后端消息推送,如果接收到版本升级的信息,则页面提示用户系统升级,让用户进行手动刷新,加载最新的页面资源

2.EventSource: EventSource与websocket有些类似,都可以进行一种实时消息通信,跟websocket不同的是,eventSource只能通过由服务端向用户端推送消息, 用户端不能主动发送消息到服务端

3.页面轮训:这种方式就比较简单,就是在页面打开后发起一个定时器轮训,定时向服务端查询版本发布情况,当有版本升级的时候即可提示用户刷新界面



经过对以上三种技术的分析我们发现:上面问题几乎都有这以下几个问题通病

1.依赖服务端提供能力支持

  1. 消耗浏览器性能,可能带来性能问题

3.不具备通用性,每次都需要进行定制化业务开发方可满足业务需求



对比发现如果有一种方案不仅可以检测用户版本升级,而且不涉及业务逻辑,还不依赖后端环境,岂不是很好;

2、方案详情

经过查询资料,和参考网络文章发现我们可以将错误捕获和文件hash比对相结合,达到我们想要的目的,具体方案如下

  1. 通过脚本封装,当页面被打开时,先请求一次当前页面index.html的内容通过内容分析,查找出页面中加载的主js脚本的hash值并记录

2.为我们的页面添加全局错误捕获,针对捕获错误问题进行分类,如果存在脚本加载404问题,则进入第3步

  1. 再次请求当前页index.html的文件内容,并获取到最新的hash值,和步骤1的存储内容进行比对,如果内容一致,则把步骤2中捕获的错误进行抛出,不做任何业务逻辑,如果不一致则代表用户所在页面进行过版本发布,此时回调用户传入的回调函数进行业务操作,这里一般是提示用户升信息,引导用户刷新即可。



纯前端方案检测系统升级(望指教)



3、适用场景

当然,此方案并不是完美的,目前识别到以下场景可能并不适用:

1.使用CDN资源增量发布的系统,此时虽然版本进行了更新,但是静态资源任然存在,此时并不会报出脚本加载错误的问题

2.传统项目,未使用hash命名静态资源的项目

4、核心代码示例-如何检测代码脚本错误

错误示例:为script标签添加onerror错误监听,这种方法理论上来说谁可以监听到脚本加载错误的,但是现在前端工程普遍使用前端工程化工具构建,所以为自动化生成的文件都添加上错误监听本身就是一件很费力的事情,因此不推荐;

纯前端方案检测系统升级(望指教)





<!DOCTYPE html>
<html lang="en">
<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">
  //test 脚本并不存在因此加载会报错
  <script onerror="console.log('脚本加载错误')" src="./test.js"></script>
  <title>测试页面</title>
</head>
<body>
  
</body>
</html>

正确示例:

核心点:1、添加全局监听,并使用捕获阶段捕获错误

2、区分出脚本加载错误和其他类型的JS错误, 通过时间对象里面的target事件对象来判断,另外就是这个事件本身是有区别的,普通js错误的时间对象是一个EVENT对象,而类似于throw Error这种事件对象本身是一个 Error Event对象,因此我们可以通过这个来区分我们想要的时间类型



纯前端方案检测系统升级(望指教)





<!DOCTYPE html>
<html lang="en">

<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">
  <!-- 这段错误脚本一定要写在脚本的第一个,防止错误发生时,全局事件还未注册 -->
  <script>
    window.addEventListener('error', (e) => {
      const tag = e.target;

      if (tag.tagName === 'SCRIPT' && !(e instanceof ErrorEvent)) {
        console.log('脚本加载错误',e)
      } else {
        console.log('其他类型js错误',e)
      }
    //因为脚本加载错误时间不冒泡,所以这一一定要在捕获阶段捕捉在这个错误,因此这里一定要将第二个参数设置成true  
    }, true)
  </script>
  <script src="./test.js"></script>
  <script>
    throw new Error('报错了')
  </script>
  <title>测试页面</title>
</head>

<body>