错误监听
1. 基于 onerror、unhandledrejection 事件错误监听
window.onerror属于 DOM0 级别接口,主要捕获 JS 运行时错误。addEventListener('error', ..., true)可在捕获阶段监听,能额外捕获资源加载错误(图片、脚本、样式、字体等)。- 通过
event.target.src || event.target.href可区分资源错误与普通运行时错误。 unhandledrejection用于捕获未被处理的 Promise 拒绝错误。
javascript
window.addEventListener(
'error',
(event) => {
const target = event.target;
// 判断错误类型
const isResourceError =
!!target &&
('src' in target || 'href' in target);
if (isResourceError) {
const url =
target.src ||
target.href ||
'unknown';
captureException({ type: 'resource_error', url });
return;
}
captureException(event.error || new Error(event.message));
},
// 配置 true 支持捕获阶段监听
true
);
window.addEventListener('unhandledrejection', (event) => {
// 统一错误上报入口
captureException({ type: 'unhandledrejection', reason: event.reason });
});
2. 跨域脚本错误捕获
跨域脚本默认可能只看到 Script error。要拿到完整堆栈,需要前后端同时配置:
- 前端标签增加
crossorigin。 - 服务端返回对应 CORS 头。
html
<script src="https://cdn.example.com/app.js" crossorigin="anonymous"></script>
http
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Credentials: true
注意 :若使用 crossorigin="use-credentials",服务端必须允许携带凭据,且 Access-Control-Allow-Origin 不能为 *。
3. 异步任务错误捕获(包装全局 API)
setTimeout、setInterval、requestAnimationFrame 等异步回调可通过包装函数统一兜底,且不改变原有行为。
javascript
function wrap(fn) {
return function wrapped(...args) {
try {
// 保留 this 和参数,保持原函数行为一致
return fn.apply(this, args);
} catch (err) {
// 捕获后继续抛出,避免吞错
captureException(err);
throw err;
}
};
}
const originalSetTimeout = window.setTimeout;
window.setTimeout = function (fn, delay, ...rest) {
if (typeof fn === 'function') {
return originalSetTimeout(wrap(fn), delay, ...rest);
}
return originalSetTimeout(fn, delay, ...rest);
};
4. 请求错误监听(XHR / Fetch)
XHR 拦截
javascript
if (window.XMLHttpRequest) {
const originalSend = XMLHttpRequest.prototype.send;
function handleXhrEvent(event) {
const xhr = event.currentTarget;
if (xhr && xhr.status >= 400) {
captureException({
type: 'xhr_error',
status: xhr.status,
responseURL: xhr.responseURL,
});
}
}
XMLHttpRequest.prototype.send = function (...args) {
// 统一监听请求失败与异常中断
this.addEventListener('error', handleXhrEvent);
this.addEventListener('abort', handleXhrEvent);
this.addEventListener('load', handleXhrEvent);
return originalSend.apply(this, args);
};
}
Fetch 拦截
javascript
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function (...args) {
return originalFetch
.apply(this, args)
.then((res) => {
if (!res.ok) {
captureException({
type: 'fetch_error',
status: res.status,
url: res.url,
});
}
return res;
})
.catch((error) => {
captureException({ type: 'fetch_exception', error });
throw error;
});
};
}
5. Vue 错误监听
Vue 提供组件级 errorCaptured 与应用级 app.config.errorHandler。通常建议:
errorCaptured:用于局部兜底和降级。errorHandler:用于全局聚合与统一上报。
javascript
app.config.errorHandler = (err, instance, info) => {
const componentName =
instance?.$options?.name ??
instance?.$options?.__name ??
'AnonymousComponent';
captureException({
type: "vue_error",
error: err,
component: componentName,
hook: info,
});
// 如需保留默认日志输出,可手动补充
console.error(err);
};
无侵入拦截(Monkey Patching)
监控 SDK、埋点库常使用 Monkey Patching 实现无侵入注入。
本质是保留原函数引用,在外层扩展逻辑后再调用原始行为。
- 保留
this与参数上下文。 - 提供
unpatch,支持恢复原行为(便于测试和热更新)。 - 通过标记位避免重复 patch。
javascript
function patchMethod(obj, name, replacement) {
const original = obj[name]
if (typeof current !== 'function') return () => {};
if (current.__patched__) return () => {};
obj[name] = function(...args) {
// 把原函数 original 传进去,让 replacement 决定何时调用
return replacement.call(this, original.bind(this), args)
}
// 保存引用方便回退使用
obj[name].__original = original
obj[name].__patched = true
// 返回 unpatch 函数
return () => {
obj[name] = original
delete obj[name].__original
delete obj[name].__patched
}
}
// 使用示例:拦截 fetch,返回的unpatch 函数讲内容还原成默认值
const unpatch = patchMethod(window, 'fetch', (original, args) => {
const [url, options] = args
const start = Date.now()
// 调用原始函数指向默认行为,然后修改返回内容
return original(url, options).then(resp => {
// 这里是记录请求示例
recordRequest(url, resp.status, Date.now() - start)
return resp
})
})
常用场景:
fetch/XMLHttpRequest:采集请求耗时、状态码、错误率,注入追踪头。console.*:采集日志与面包屑,避免在 patch 内递归调用自身。history.pushState/history.replaceState:路由变化监听,配合popstate。addEventListener:采集交互行为,区分event.target与event.currentTarget。setTimeout/setInterval/requestAnimationFrame:错误兜底与调度耗时分析。
总结
- 错误捕获 :通过
onerror、unhandledrejection、资源错误监听、请求拦截与 Vue 全局处理器,基本覆盖前端主要错误来源。 - 无侵入增强:通过 Monkey Patching 在不改业务调用方式下扩展监控能力,保留原函数行为,同时实现可恢复 patch。