用原生 JS + Vue 实现一套可复用的前端错误监控系统

0 阅读4分钟

在这里插入图片描述

摘要

随着前端项目变得越来越复杂,一点小小的异常都有可能导致页面白屏、功能不可用,甚至用户直接流失。为了尽早发现这些问题,我们需要在项目中构建一个前端错误监控系统,能自动捕获错误、记录上下文、上报后端,辅助我们快速定位问题,保障线上应用的稳定运行。

引言

虽然市面上已有如 SentryFundebug 等成熟的监控平台,但对于部分项目(特别是中小项目)来说,要么成本太高,要么无法灵活定制。这时候,自己搭建一套“够用”的前端监控系统,就是非常实际的选择。

本文将手把手带你实现一个前端错误监控系统,包括常见错误捕获方法、信息收集、后端上报接口以及典型的实际使用场景。

前端错误监控系统的搭建思路

全局错误捕获方式

JavaScript 运行时错误

// 捕获运行时错误(语法错误、null 访问等)
window.onerror = function (message, source, lineno, colno, error) {
  reportError({
    type: 'js_error',
    message,        // 错误信息
    source,         // 出错文件
    lineno,         // 行号
    colno,          // 列号
    stack: error?.stack // 错误堆栈
  });
};

解释:当你访问未定义变量、或函数中抛出异常,这个函数会第一时间捕获它,并将错误信息打包给 reportError 函数。

静态资源加载错误(图片、CSS、JS)

window.addEventListener('error', function (e) {
  // 过滤 JS 运行错误,保留资源加载错误
  if (e.target && e.target !== window) {
    reportError({
      type: 'resource_error',
      tagName: e.target.tagName,       // 资源标签名(如 IMG / SCRIPT)
      src: e.target.src || e.target.href // 资源地址
    });
  }
}, true);

解释:这个事件在 true 捕获阶段触发,可以监听资源文件加载失败,比如图片 404 或字体文件加载不到。

Promise 未处理异常

window.addEventListener('unhandledrejection', function (e) {
  reportError({
    type: 'promise_error',
    reason: e.reason?.message || JSON.stringify(e.reason) // 兼容各种 Promise 报错内容
  });
});

解释:这类错误通常出现在你 fetch() 或异步函数没有 .catch() 的时候,可能会被忽略,非常隐蔽。

错误信息统一上报接口

我们将捕获到的错误发送到服务器接口,一般推荐使用异步请求。

function reportError(errorInfo) {
  try {
    const payload = {
      ...errorInfo,
      url: location.href,
      userAgent: navigator.userAgent,
      time: Date.now()
    };

    // 推荐使用 sendBeacon,不阻塞主线程
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/log/error', JSON.stringify(payload));
    } else {
      fetch('/api/log/error', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });
    }
  } catch (e) {
    console.warn('reportError 发送失败', e);
  }
}

说明:

  • sendBeacon():适合在页面卸载时使用,保证日志能发出去。
  • 加了 try-catch 防止监控代码本身出错。

在框架中接入错误监控

Vue 应用错误捕获(Vue 3)

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// Vue 全局错误处理
app.config.errorHandler = (err, instance, info) => {
  reportError({
    type: 'vue_error',
    message: err.message,
    stack: err.stack,
    info
  });
};

app.mount('#app');

说明:配合 Vue 的生命周期,能捕获组件内部的渲染错误、生命周期钩子中的异常。

用户提示与异常处理

你可以根据错误类型,给用户展示提示信息,而不是直接页面崩溃。

function handleUserNotice(errorInfo) {
  if (errorInfo.type === 'js_error') {
    alert('页面出现了一些小问题,请刷新后重试');
  }
}

还可以加入“自动恢复机制”或“点击重试按钮”。

典型使用场景举例

场景 1:用户打开页面就白屏,控制台报错

可能是初始化代码异常。我们可以在入口加上 try-catch:

try {
  app.mount('#app');
} catch (e) {
  reportError({
    type: 'entry_error',
    message: e.message,
    stack: e.stack
  });
}

场景 2:资源加载失败导致图标不显示

在加载资源时增加标识:

<link rel="stylesheet" href="cdn/font-awesome.css" data-monitor="true">

然后在错误监听中判断:

if (e.target.dataset.monitor === 'true') {
  reportError({
    type: 'resource_error',
    src: e.target.href,
    tag: e.target.tagName
  });
}

场景 3:用户点击按钮后无响应

可能是某个异步请求失败但未 catch:

fetch('/api/data')
  .then(res => res.json())
  .then(data => {
    processData(data);
  });
  // 忘记加 .catch

通过 unhandledrejection 我们可以自动捕获到:

window.addEventListener('unhandledrejection', function (e) {
  reportError({
    type: 'promise_error',
    reason: e.reason
  });
});

QA 问答环节

Q:这个监控系统会影响页面性能吗?

不会。错误上报是异步的,不会阻塞主线程。你还可以批量收集再统一发送,进一步降低影响。

Q:如果错误很多,会不会一直重复上报?

建议加入简单去重机制,例如根据 message + stack 做 hash,存在缓存中,5分钟内相同的不再重复上报。

Q:如何知道哪些页面报错多?

你可以在错误上报接口中增加字段,比如:

pagePath: location.pathname

后端聚合统计后,就能知道哪些页面最容易出问题。

总结

搭建前端错误监控系统并不难,只要掌握:

  • 错误的捕获机制(onerrorerror 事件、Promise 异常等)
  • 上下文数据收集
  • 合理的后端上报方式
  • 框架错误集成(Vue、React 等)

这样的监控系统,可以帮助你第一时间发现问题、快速定位、避免用户流失。