前端覆盖式发布引发的使用体验提升

我们公司前端采用的是覆盖式发布,过程就是:每次上线时构建好前端项目,将构建产物丢给运维,运维直接用新构建直接替换掉线上的版本(一般小作坊应该都是这么搞的...为啥这么搞?我想理由应该是想同的,在此我就不过多解读了)简单粗暴的背后隐患是很大的:假设这么个场景,有用户a和用户b有在使用我们的平台,有一天某时某刻,a正在使用我们的平台,成功登陆后,开始操作balabala,然后a没有关浏览器窗口,也没有关电脑,然后出去嗨了...在a嗨的同时,我们公司可爱的前端同学修复了一个bug,测试好了之后上线了一版,恰巧上线完成之后,b用户也来访问我们的平台,跟a一样的操作,没有出什么问题,但是a这时候继续使用我们平台的时候,问题就来了,发现点击好多页面没有反应,万幸的是a会武术,a打开f12后发现好多js文件404了?什么 ,我嗨之前不是好好的吗?怎么嗨完回来就gg了...

 

分析

仔细详细详细,这种场景下,用户a的问题是不是偶发还是必然?当然是必然了。。。不要给方案上的缺陷找借口了。。。现在前端开发一般是webpack构建项目,在html只会写一个入口的js文件,多个页面的js文件都会被webapck切分代码在配置中映射起来,通过webpack.require来进行懒加载。比方说用户页面和设置页面,用户访问用户页面(没有打开设置页面),这个时候我们修改设置页面并更新一个新版本,用户在打开设置页面的时候就会出现js404:入口js文件记录的设置页面还是修改前的js哈希文件,我们上线后会覆盖掉之前的文件,因此用户就会报js404错误。。。

解决

发布方案估计在很长一段时间没得改了。。。那就只能在现有基础上改了。。。在网上查找了解到有人使用 addEventListener 这种方法,第三个参数必须为true,使用捕获传播(因为是监听资源的错误),如下

window.addEventListener("error",  handler, true)

思路就是,如果监听到错误,判断是否为js错误,然后获取js的src值,进行一次HEAD请求,确认是否http返回状态码为404,如果是404的话提示用户更新版本(刷新页面)后再使用:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
  /**
   * 判断js资源是否返回404状态码
   * @param url
   * @return {Promise<any>}
   */
  function isScript404(url) {

    return new Promise((resolve, reject) => {
      let xhr = new XMLHttpRequest()
      xhr.open('HEAD', url, false)

      xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 404) {
          resolve(true)
          return
        }

        resolve(false)
      }

      xhr.send()
    })
  }

  /**
   * 处理错误函数
   * @param e
   */
  async function handleError(e) {
    console.log('script resource load error....')

    let script = e.target
    let SCRIPT = 'SCRIPT'
    let tagName = script.tagName
    let src = script.src

    // js资源网络请求失败
    if (SCRIPT === tagName) {
      let bool = await isScript404(src)
      if (!bool) {
        return
      }
      console.log('更新版本,请刷新页面')
    }
  }

  // 绑定事件
  window.addEventListener('error', handleError, true)
</script>
<script src="b.js"></script>
</body>
</html>
posted @ 2020-01-19 13:38  韩帅  阅读(591)  评论(0编辑  收藏  举报