错误上报方式

前端错误上报的两种常用方式:Image Beacon 和 sendBeacon

问题

前端错误上报有哪两种常用方式?各有什么特点?

解答

方式一:Image Beacon(图片打点)

// 使用 Image 对象发送上报请求
function reportByImage(url, data) {
  const img = new Image();
  // 将数据拼接到 URL 参数中
  const params = Object.keys(data)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
    .join('&');
  
  img.src = `${url}?${params}`;
}

// 使用示例
reportByImage('https://log.example.com/report', {
  type: 'error',
  message: 'Uncaught TypeError',
  page: location.href,
  timestamp: Date.now()
});

方式二:Navigator.sendBeacon

// 使用 sendBeacon 发送上报请求
function reportByBeacon(url, data) {
  // sendBeacon 支持发送 Blob、FormData、字符串等
  const blob = new Blob([JSON.stringify(data)], {
    type: 'application/json'
  });
  
  // 返回 boolean,表示是否成功加入发送队列
  return navigator.sendBeacon(url, blob);
}

// 使用示例
reportByBeacon('https://log.example.com/report', {
  type: 'error',
  message: 'Uncaught TypeError',
  page: location.href,
  timestamp: Date.now()
});

完整的错误上报封装

class ErrorReporter {
  constructor(reportUrl) {
    this.url = reportUrl;
  }

  // 优先使用 sendBeacon,降级使用 Image
  report(data) {
    const payload = {
      ...data,
      timestamp: Date.now(),
      url: location.href,
      userAgent: navigator.userAgent
    };

    // 优先使用 sendBeacon
    if (navigator.sendBeacon) {
      const blob = new Blob([JSON.stringify(payload)], {
        type: 'application/json'
      });
      const success = navigator.sendBeacon(this.url, blob);
      if (success) return;
    }

    // 降级使用 Image
    const img = new Image();
    img.src = `${this.url}?data=${encodeURIComponent(JSON.stringify(payload))}`;
  }
}

// 使用
const reporter = new ErrorReporter('https://log.example.com/report');

// 监听全局错误
window.onerror = function(message, source, lineno, colno, error) {
  reporter.report({
    type: 'runtime',
    message,
    source,
    lineno,
    colno,
    stack: error?.stack
  });
};

// 监听 Promise 未捕获错误
window.addEventListener('unhandledrejection', function(event) {
  reporter.report({
    type: 'promise',
    message: event.reason?.message || String(event.reason),
    stack: event.reason?.stack
  });
});

关键点

  • Image Beacon:兼容性好,不受跨域限制,但只能 GET 请求,数据量有限
  • sendBeacon:异步非阻塞,页面卸载时也能可靠发送,支持 POST
  • sendBeacon 优势:不会延迟页面卸载,不影响下一个页面的加载性能
  • 降级策略:优先 sendBeacon,不支持时降级到 Image
  • 两者都不需要处理响应:上报请求通常是”发完即忘”的单向通信