常见异常

常见异常

‘xxx’ is undefined

造成 ‘xxx’ is undefined 的主要原因有两种:

  • 其实大家自己写代码的时候,现在各种 Lint 工具都可以帮我们规避掉 ‘xxx’ is undefined 这类问题,但为什么还是会出现呢?常见的一个情况就是后台接口返回的数据是 JSON 格式,加上某个后端服务一旦出现了问题,导致返回的数据异常。这是遇到的最多的一个情况。

  • 大家现在的应用里面应该都有分包的工作,一般都会把一些主体的包单独拆出来,一旦这种包加载失败,就会出现 ‘xxx’ is undefined

数据校验

后端接口的数据不可信,该做的判断一定不能少。在引入一些其他的优化方案的时候,要考虑一下它的副作用。可能这个问题不是你导致的,但是别人的问题可能会导致你的问题!

CDN 劫持

我们有个业务在没有任何发布的情况下,然后它的页面加载的失败率从 0.5% 到了 10%。我们就找 CDN 的同学去看哪里出了问题,CDN 也没异常。后来几经波折,多方定位,发现某一个节点上面的文件指纹和主栈的 CDN 节点的文件指纹是不一样的。也就是说这个节点的 CDN 文件被篡改了,被劫持了。大家知道 CDN 实际上是主栈要推到各个 CDN 栈上去的。虽然在我们的页面上面是走 HTTPS,但是从主栈把资源推到各个 CDN 上的时候,为了追求速度是不会去走 HTTPS 的,而是走 HTTP。其实这就给了一些运营商可趁之机,在这个阶段就把 CDN 上的资源做了一个篡改,导致资源上了 HTTPS 还是被劫持。

当然,我们肯定知道会存在这种劫持情况。所以很早就加了一个 SRI 的方案,做防劫持的处理。简单讲一下,比如 SRI 会在 Script 标签上面有一个指纹,当你通过构建工具生成一个 JS 文件的加密后的一个指纹,然后在服务器下载下来的文件上面,也会对那个文件做一个指纹,然后再和这个标签上面的文件做指纹的比对。如果发现不一样,就会认为这个文件被篡改了,是被劫持的。当然防劫持的目的达到了,但同时也带来了新的问题——白屏。因为浏览器认为资源不可信,就不会去执行 JS 了,不会执行这些 JS 就不会去做任何异步尝试,也就说 JS 最终还是被当作加载失败来处理了。

当 SRI 失败的时候,还是会触发 Script Error 事件。所以我们要做一些补充的方案,如果 Script 触发 onError,我们会向主栈拉取一次 JS 资源,不是 CDN 节点上的 JS,然后拉取主栈的 JS 的话,我们会取他前头部的几个字节以及尾部的字节,和我们现有的 CDN 上的资源做一个对比,全文对比太长了,我们做前后的这种字符检测就可以了。如果检查字符不一样,我们就直接使用主栈的资源进行加载,然后再走到上报的平台。

<script
  type="text/javascript"
  src="..."
  integrity="sha256-xxx sha384-xxx"
  crossorigin="anonymous"
  onerror="loadScriptError.call(this, event)"
  onsuccess="loadScriptSuccess"
></script>

Script Error

  • iOS 中跨域的异步脚本的报错信息在 window.onError 中是捕获不到的。

举个例子,在 a 域名的页面下引入了 b 域名的脚本,b 域名的脚本在执行 setTimeout 中的一段代码,出现了异常。window.onError 是无法获取到这个错误的。目前针对这种情况的解决方法,只能对异步脚本中的代码手动地抛出错误进行捕获。

  • 通过 native 代码执行的脚本报错,是无法被捕获的。

对于 Hybird 的 APP 中一些复杂的页面,客户端都会去调一些我们 JS 的代码,执行一些能力。客户端同学可能网上搜了一段代码,并不会告诉你这段代码有什么,然后本地跑执行没问题,所以他就直接加上去了。但比如说他回调你页面里面的一个接口,windows 怎么 xxx 执行一下。但其实可能你代码有什么问题,或者是他在你的代码声明之前就执行了,这个时候实际上也会有报错且没有 Script Error,所以这种问题你就需要联合客户端这边,要对他们执行的这种 JS 代码做一些保护,常见的就是执行前,先对 JS 回调进行检查。

  • 离线缓存

因为一些离线缓存实现的问题不太对,现在一些大的 App 里面,都有做这种离线缓存功能。理论上来说,把接口拦截掉,返回一个本地的资源,当然资源的 Header 是客户端来设置的,这个时候不会设置跨域头,也会报 Script Error 。为了解决这个问题,我们做了一个临时策略,把离线包里面所有的资源都换成主域的资源,解决历史版本中的问题,同时推动客户端添加缓存头。

下一页