作为前端开发可能会经常遇到,产品、运营、用户吐槽反馈问题:为什么我的页面空白、页面没有更新,为什么每次都要清缓存、要点刷新按钮。

一、为什么会白屏、资源报错?

h5 ios白屏 微信h5白屏_h5 ios白屏

h5 ios白屏 微信h5白屏_缓存_02

我们每次打包都会生成一个index.html文件,这个是项目的入口文件。在index.html文件中会嵌入我们的静态资源,比如manifest.xxxjs和app.xxx.js文件,问题就出在了这里,很多浏览器特别是微信浏览器会缓存我们的index.html文件,也即同时缓存了manifest.xxxjs和app.xxx.js文件。但是我们每次执行npm run build构建命令,manifest.xxxjs和app.xxx.js都会重新生成,每次都不一样,构建一次后原来的静态资源相当于已经被删除不存在了,而有些浏览器却又缓存了index.html文件,旧的文件已经被删除,所以文件找不到,js无法继续执行,最终页面挂起白屏。

那怎么让页面在遇到不存在资源的时候,能够主动去刷新呢?是否能够在静态资源加载失败的时候能够主动刷新,去重新请求资源呢?

我想到的是使用 window.addEventListener监听错误,而且是在manifest.xxxjs和app.xxx.js 这两种类型的静态资源失败时才去重新刷新,并且刷新三次(如果一直报错,刷新再多也没有用,做了限制处理),其他错误根据需要进行处理。

通过e.target.outerHTML可以打印看到当前是哪种类型的静态资源报错

h5 ios白屏 微信h5白屏_缓存_03

在index.html文件中加入以下监听代码

<script>
    // 错误监听
    window.addEventListener('error', (e) => {
      // alert(e.target.outerHTML)
      // 类似manifest.js和app.js等静态资源报错
      if(e.target.outerHTML.indexOf('js/manifest.') != -1 || e.target.outerHTML.indexOf('js/app.') != -1){
        var errorTime = sessionStorage.getItem('mainAppErrorTime')
        if(errorTime&&errorTime>0){
          errorTime++
        } else{
          errorTime = 1
        }
        var timer = setTimeout(function() {
          // 错误次数存在缓存,刷新超过3次不会继续刷新。退出页面重进后,次数重置
          sessionStorage.setItem('mainAppErrorTime', errorTime)
          window.location.reload()
        }, 1000);
        if(errorTime&&errorTime>=3){
          clearTimeout(timer) // 清空定时器
        }
      }
    }, true);

  </script>

②同时遇到静态资源报错的还有下面这种情况

Loading chunk 0 failed

h5 ios白屏 微信h5白屏_版本号_04

这也是静态资源没有更新导致的

h5 ios白屏 微信h5白屏_h5 ios白屏_05

这一类型的错误vue-router 捕获错误并进行处理。在vue-router,index.js中加入以下代码

// 当捕获到Loading chunk {n} failed的错误时我们重新渲染目标页面
router.onError((error) => {
  console.log('router onerror', error)
  const pattern = /Loading chunk (\S)+ failed/g
  const isChunkLoadFailed = error.message.match(pattern)
  if (isChunkLoadFailed) {
    // const targetPath = router.history.pending.fullPath;
    // router.replace(targetPath);
    // 用路由的replace方法,并没有相当于F5刷新页面,失败的js文件并没有从新请求,会导致一直尝试replace页面导致死循环,
    // 而用 location.reload 方法,相当于触发F5刷新页面,虽然用户体验上来说会有刷新加载察觉,但不会导致页面卡死及死循环
    location.reload()
  }
})

二、页面为什么没有更新,刷新了也没有用

发版后即使刷新了也没有更新页面,特别是微信浏览器的缓存机制,缓存很严重。

所以最好的方案还是在 页面路径后面 添加版本号或者时间戳。

但前端的路由存在两种,一种是history路由,另外一种是带#的hash路由,很多人反馈为什么我在页面后面加上了版本号或者时间戳也没有效果呢,有可能你用的是 hash路由,因为hash路由#后面的参数无论怎么变也是同一个地址。

①History路由后面添加时间戳或者版本号

https://www.xxx.com?version=123456

②如果是hash路由,则可以在#号前加时间戳或者版本号

举例:原访问路径是  https://www.xxx.com/policy/#/abc.vue,policy项目名称,前端项目放在服务器data/hdwork-h5目录下。

在#前面加版本号就变成:https://www.xxx.com/policy/123/#/abc.vue,123是版本号的举例,这个可以自己定,是个数字就可以,这种做法简单,只需要nginx再多加一个匹配数字的配置就可以。以下是nginx项目的配置参考,具体以自己项目定义。这里只给出了匹配/policy/\d+ 匹配数字的案例,不加数字的/policy 就是原逻辑。

这样 加版本号https://www.xxx.com/policy/{版本号}/#/abc.vue 和 不加版本号https://www.xxx.com/policy/#/abc.vue,这两种方式都是可以正常访问的

h5 ios白屏 微信h5白屏_h5 ios白屏_06

原理就是 当我们请求带版本号的请求路径https://www.xxx.com/policy/123/#/abc.vue时,会自动重写成 正常的访问路径