基础系列-onerror处理

理论

GlobalEventHandlers.onerror

用于处理 error 事件。

Error事件的事件处理程序 (en-US),在各种目标对象的不同类型错误被触发:

  • JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()
  • 当一项资源(如<img><script>加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到 window,不过(至少在Firefox中)能被单一的window.addEventListener (en-US)捕获。可以使用捕获的方式监听到资源加载失败。

加载一个全局的error事件处理函数可用于自动收集错误报告。

由于历史原因,window.onerrorelement.onerror接受不同的参数。

window.onerror

window.onerror = function(message, source, lineno, colno, error) { ... }

函数参数:

  • message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event
  • source:发生错误的脚本URL(字符串)
  • lineno:发生错误的行号(数字)
  • colno:发生错误的列号(数字)
  • errorError对象(对象)

若该函数返回true,则阻止执行默认事件处理函数。

这种方式只能捕获脚本错误问题。

window.addEventListener(‘error’)

window.addEventListener('error', function(event) { ... }) // 冒泡方式只能获取到脚本错误问题
window.addEventListener('error', function(event) { ... }, true) // 捕获方式可以获取到脚本错误和资源加载失败的问题

ErrorEvent 类型的event包含有关事件和错误的所有信息。

ErrorEvent 事件对象在脚本发生错误时产生,它可以提供发生错误的脚本文件的文件名,以及发生错误时所在的行号等信息。

Event 类型为资源加载失败返回的类型。

Element.onerror

element.onerror = function(event) { ... }

element.addEventListener(‘error’)

可以监听到脚本执行错误或者资源未加载的错误。

html

<div class="controls">
  <button id="img-error" type="button">Generate image error</button>
  <img class="bad-img" />
</div>

<div class="event-log">
  <label>Event log:</label>
  <textarea readonly class="event-log-contents" rows="8" cols="30"></textarea>
</div>

js

const log = document.querySelector('.event-log-contents');

const badImg = document.querySelector('.bad-img');
badImg.addEventListener('error', (event) => {
    log.textContent = log.textContent + `${event.type}: Loading image\n`;
    console.log(event)
});

const imgError = document.querySelector('#img-error');
imgError.addEventListener('click', () => {
    badImg.setAttribute('src', 'i-dont-exist');
});

window.addEventListener(‘load’)

当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件。

window.addEventListener('load', (event) => {
  console.log('page is fully loaded');
});

其他

跨域问题

监听设置的位置问题

实践

公司有做一个白屏检测,检测资源是否未加载。

逻辑为:

  1. 先在入口文件中挂载特定的 dom,设置 window.addEventListener(‘load’, callback),监听事件,当首页加载完毕后,响应 load 监听事件判断 dom 是否挂载,如果没有在 dom 树上找到目标元素,则表示发生了白屏事件。

  2. 设置 window.addEventListener(‘error’, callback, true) 使用捕获的方式监听到资源加载失败。其中判断 cdn 加载失败,则会重新创建 link 和 script 标签重新加载,如果二次重载的资源错误则表示用户进入了网络链接不稳定的状态。

  3. 设置 window.addEventListener(‘load’, callback, false) 监听页面资源加载完成的情况。

  4. 手动判断是否资源加载的问题,然后将问题上报到 sentry。

    但是不排除代码中出现 undefined 导致的白屏问题,比如说 A 页面跳转到 B 页面需要传参,但是发生了 undefined 的问题,则会直接导致 B 页面白屏。

DEMO

<!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">
  <title>Error</title>
</head>
<body>
<div>
  测试 error
  <img src="someimage.png" onerror="handleError(this)" />
  error
  <div class="controls">
    <button id="img-error" type="button">Generate image error</button>
    <img class="bad-img" />
  </div>
  
  <div class="event-log">
    <label>Event log:</label>
    <textarea readonly class="event-log-contents" rows="8" cols="30"></textarea>
  </div>
  <button onclick="makeError()">调用error</button>
</div>
<script>
  function handleError(e) {
    console.log('handleError: ', e);
  }

  window.onerror = function(msg, url, line, col, error) {
    // 直接将错误打印到控制台
    console.log('window.onerror: ', msg, url, line, col, error)
  }

  window.addEventListener('error', function(event) { 
    console.log('addEventListener: ', event, event instanceof ErrorEvent);
    console.log('addEventListener after: ', event.target.baseURI);
  }, true)

  window.addEventListener('load', (event) => {
    console.log('page is fully loaded');
  });

  const log = document.querySelector('.event-log-contents');

  const badImg = document.querySelector('.bad-img');
  badImg.addEventListener('error', (event) => {
      log.textContent = log.textContent + `${event.type}: Loading image\n`;
      console.log('addEventListener badImg: ', event)
  });

  const imgError = document.querySelector('#img-error');
  imgError.addEventListener('click', () => {
      badImg.setAttribute('src', 'i-dont-exist');
  });

  function makeError () {
    var name = "geoff"
    var msg = "Hi, " + Name
    console.log(msg)
  }
</script>
<script src="someimage.js" onerror="handleError(this)"></script>
</body>
</html>

白屏方案

参考链接:https://segmentfault.com/a/1190000040236233

参考链接

GlobalEventHandlers.onerror:https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalEventHandlers/onerror

GlobalEventHandlers:https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalEventHandlers

ErrorEvent:https://developer.mozilla.org/zh-CN/docs/Web/API/ErrorEvent