在讲错误收集方式之前,先理清一下前端编码时有那些常见错误

 

 

 

 


前端错误收集是保障用户体验、排查线上问题的关键手段。本文将从多个角度详解前端错误收集的各种方式,包括不同框架(如 React、Vue)、JavaScript 执行环境中的同步/异步/Promise 错误、资源加载错误(如图片、script 加载失败)等。以下是全面的错误捕获机制总结:


 

 

🌐 一、通用错误收集方式(原生 JavaScript)

1. try...catch 捕获同步代码错误

 1try {
 2  
 3  let result = a.b; 
 4} catch (err) {
 5  console.error('捕获同步错误:', err);
 6}

注意:

  • 只能捕获同步代码错误,不能捕获异步或事件回调内的错误。

2. window.onerror

 1window.onerror = function (message, source, lineno, colno, error) {
 2  console.error('window.onerror 捕获:', { message, source, lineno, colno, error });
 3};

特性:

  • 可捕获运行时错误,包括未捕获的异常。
  • 跨域脚本错误默认不会暴露具体信息,需设置 crossorigin 属性和响应头。

3. window.addEventListener('error') 捕获资源加载错误

 1window.addEventListener('error', function (event) {
 2  if (event.target instanceof HTMLScriptElement || event.target instanceof HTMLImageElement) {
 3    console.error('资源加载错误:', event.target.src || event.target.href);
 4  } else {
 5    console.error('运行时错误:', event.message);
 6  }
 7}, true); 

优点:

  • 能捕获 <img>, <script>, <link> 等静态资源加载错误。

4. unhandledrejection 捕获未处理的 Promise 异常

 1window.addEventListener('unhandledrejection', function (event) {
 2  console.error('未处理的 Promise 异常:', event.reason);
 3});

场景:

  • Promise 的 .catch() 未添加或写在不合适位置。

⚛️ 二、React 中的错误捕获

1. React 16+ 错误边界(Error Boundaries)

 1class ErrorBoundary extends React.Component {
 2  state = { hasError: false, error: null };
 3
 4  static getDerivedStateFromError(error) {
 5    return { hasError: true };
 6  }
 7
 8  componentDidCatch(error, info) {
 9    console.error('React 错误捕获:', error, info);
10  }
11
12  render() {
13    if (this.state.hasError) {
14      return <h2>出错了</h2>;
15    }
16    return this.props.children;
17  }
18}

限制:

  • 只能捕获子组件生命周期、render、构造函数中的错误。

  • 不能捕获:

    • 异步代码(如 setTimeout
    • 事件处理函数错误
    • SSR 期间的错误

🐸 三、Vue 中的错误捕获

1. Vue 2.x errorHandler

 1Vue.config.errorHandler = function (err, vm, info) {
 2  console.error('Vue 错误:', err, info);
 3};

2. Vue 3.x app.config.errorHandler

 1const app = Vue.createApp(App);
 2
 3app.config.errorHandler = (err, instance, info) => {
 4  console.error('Vue3 错误:', err, info);
 5};

3. 异步函数中的错误

异步函数内的错误需要使用 try/catch 或监听 unhandledrejection


🧠 四、异步任务中的错误处理方式

1. setTimeoutsetInterval

 1setTimeout(() => {
 2  try {
 3    throw new Error('异步定时错误');
 4  } catch (err) {
 5    console.error('捕获异步错误:', err);
 6  }
 7}, 1000);

2. Promise 中的异常

 1new Promise((resolve, reject) => {
 2  throw new Error('Promise 内部异常'); 
 3});

3. async/await 异常处理

 1async function test() {
 2  try {
 3    await fetch('...');
 4    throw new Error('异步错误');
 5  } catch (e) {
 6    console.error('async/await 错误:', e);
 7  }
 8}

🖼️ 五、资源加载错误(图片、脚本等)

图片加载失败

 1<img src="wrong.png" onerror="console.error('图片加载失败')" />

脚本加载失败

 1<script src="wrong.js" onerror="console.error('JS 加载失败')"></script>

全局监听

 1window.addEventListener('error', function (e) {
 2  if (e.target.tagName === 'IMG' || e.target.tagName === 'SCRIPT') {
 3    console.error('资源加载失败:', e.target.src || e.target.href);
 4  }
 5}, true);

 

 


📦 六、前端错误上报实践建议

  1. 统一封装错误上报工具

     1function reportError(errorInfo) {
     2  
     3  fetch('/log', {
     4    method: 'POST',
     5    body: JSON.stringify(errorInfo),
     6  });
     7}
    
  2. 错误去重、限频

    • 防止相同错误反复上报。
  3. 结合 Source Map

    • 错误行号定位需要 source map 还原。
  4. 配合监控平台(如 Sentry、Fundebug、LogRocket)

    • 可视化查看错误堆栈、用户行为路径等。

✅ 总结

类型捕获方式说明
同步代码异常try...catch推荐搭配日志上报使用
全局 JS 错误window.onerror支持堆栈、来源信息
静态资源加载失败window.addEventListener('error', ..., true)捕获 <script>/<img>/<link> 加载失败
Promise 未处理异常window.unhandledrejection捕获 Promise 中未被 .catch 处理的异常
React错误边界组件捕获渲染和生命周期异常
Vueconfig.errorHandler支持全局组件异常处理
async/await 异常try/catch手动捕获为主

如需我帮你生成一个完整的错误收集 SDK、结合你项目的架构,欢迎告诉我。


下面是一个通用的前端错误收集 SDK,适用于纯 JavaScript 应用,也兼容 React、Vue 等框架。具备如下能力:

  • 捕获同步错误(try/catch)
  • 捕获未处理的 Promise 错误
  • 捕获资源加载失败(如图片、脚本)
  • 可扩展支持自定义上报方式
  • 可用于构建自己的埋点/监控平台

✅ 错误收集 SDK 示例(error-tracker.js

 1class ErrorTracker {
 2  constructor(options = {}) {
 3    this.reportUrl = options.reportUrl || '/report-error';
 4    this.extraContext = options.context || {};
 5    this.init();
 6  }
 7
 8  init() {
 9    this.bindGlobalError();
10    this.bindPromiseRejection();
11    this.bindResourceError();
12  }
13
14  bindGlobalError() {
15    window.onerror = (message, source, lineno, colno, error) => {
16      const errorInfo = {
17        type: 'js-error',
18        message,
19        source,
20        lineno,
21        colno,
22        stack: error?.stack,
23        ...this.extraContext,
24      };
25      this.report(errorInfo);
26    };
27  }
28
29  bindPromiseRejection() {
30    window.addEventListener('unhandledrejection', (event) => {
31      const errorInfo = {
32        type: 'unhandledrejection',
33        reason: event.reason?.message || String(event.reason),
34        stack: event.reason?.stack || null,
35        ...this.extraContext,
36      };
37      this.report(errorInfo);
38    });
39  }
40
41  bindResourceError() {
42    window.addEventListener(
43      'error',
44      (event) => {
45        if (event.target && (event.target.src || event.target.href)) {
46          const errorInfo = {
47            type: 'resource-error',
48            tagName: event.target.tagName,
49            url: event.target.src || event.target.href,
50            outerHTML: event.target.outerHTML,
51            ...this.extraContext,
52          };
53          this.report(errorInfo);
54        }
55      },
56      true 
57    );
58  }
59
60  report(errorData) {
61    try {
62      navigator.sendBeacon
63        ? navigator.sendBeacon(this.reportUrl, JSON.stringify(errorData))
64        : fetch(this.reportUrl, {
65            method: 'POST',
66            headers: { 'Content-Type': 'application/json' },
67            body: JSON.stringify(errorData),
68          });
69    } catch (err) {
70      console.warn('错误上报失败:', err);
71    }
72  }
73
74  
75  captureException(error, context = {}) {
76    const errorInfo = {
77      type: 'manual-capture',
78      message: error.message,
79      stack: error.stack,
80      ...this.extraContext,
81      ...context,
82    };
83    this.report(errorInfo);
84  }
85}
86
87export default ErrorTracker;

🧪 使用方法

1. 初始化

 1import ErrorTracker from './error-tracker';
 2
 3const tracker = new ErrorTracker({
 4  reportUrl: 'https://your-domain.com/log-collector',
 5  context: {
 6    appVersion: '1.0.0',
 7    userId: '123456',
 8  },
 9});

2. 手动捕获异常(适用于 try/catch 或自定义埋点)

 1try {
 2  throw new Error('业务异常');
 3} catch (e) {
 4  tracker.captureException(e, { module: 'UserProfile' });
 5}

🛠️ 后端上报接口示例(Node.js)

 1app.post('/report-error', express.json(), (req, res) => {
 2  console.log('错误上报:', req.body);
 3  
 4  res.status(200).send('ok');
 5});

💡 可扩展建议

  • 加入节流、去重机制(防止重复错误刷爆接口)
  • 支持用户行为轨迹记录(如点击、页面跳转)
  • 上报前过滤第三方脚本错误
  • 使用 Source Map 解码堆栈信息
个人笔记记录 2021 ~ 2025