
背景
我们想实现一个 Tampermonkey 插件,当发现某个接口报错则使用替代接口。比如某个 emojisearch 网站接口挂了,我们想替换成 kimi 或 deepseek 的接口,但是我们没法修改这个网站的源码。
其次我们经常在 Chrome 控制台的 Network 面板看到接口报错 500,如何监听这类错误呢?
GET https://example.com/api/completion?query=3 500 (Internal Server Error)
如何实现?可以使用 PerformanceObserver
ts
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log('entry', entry)
if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {
if (entry.responseStatus >= 400) {
console.error('网络请求错误:', entry.name, '状态码:', entry.responseStatus);
}
}
});
});
observer.observe({ type: 'resource', buffered: true });
注意点 :
entry.initiatorType
会报 ts 错误,因为 entry 的类型被误认为是PerformanceEntry
没有该字段,TS 无法做到如此精确的推导问题 github.com/microsoft/T... 。从逻辑上看 type: resource 则 entry 的类型为PerformanceResourceTiming
则自然有initiatorType
。
PerformanceResourceTiming
继承自PerformanceEntry
我们可以通过@ts-expect-error https://github.com/microsoft/TypeScript/issues/58644
或更好的方式采用 type assertion。
diff
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
+ const entry = perEntry as PerformanceResourceTiming;
console.log('entry', entry)
if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {
if (entry.responseStatus >= 400) {
console.error('网络请求错误:', entry.name, '状态码:', entry.responseStatus);
}
}
});
});
observer.observe({ type: 'resource', buffered: true });
我们封装下"检测网络请求异常"的函数。
ts
function observeNetworkError(when: (entry: PerformanceResourceTiming) => boolean, callback: (entry: PerformanceResourceTiming) => unknown) {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((perfEntry) => {
const entry = perfEntry as PerformanceResourceTiming;
if (when(entry)) {
callback(entry)
}
});
});
observer.observe({ type: 'resource', buffered: true });
return () => {
observer.disconnect()
}
}
用法:
ts
const doSthOnNetworkError = (entry: PerformanceResourceTiming) => {
console.error('网络请求错误:', entry.name, '状态码:', entry.responseStatus);
}
const disconnect = observeNetworkError(({ initiatorType, responseStatus }) => {
return (initiatorType === 'fetch' || initiatorType === 'xmlhttprequest') && responseStatus >= 400
}, doSthOnNetworkError)
// 当不需要监听的时候
disconnect()
代码解释
代码解释
创建一个 PerformanceObserver
来监控资源加载性能指标,重点解释下 type
、buffered
和 initiatorType
参数的作用:
type: 'resource'
- 含义:指定要观察的性能条目类型
- 作用 :这里设置为
'resource'
表示观察所有资源加载的性能数据 - 其他常见类型 ,每一种类型都对应一个 interface
PerformanceXxxTiming
,更多见 PerformanceEntry: entryType property - MDN:'largest-contentful-paint'
- interfaceLargestContentfulPaint
'navigation'
- 页面导航性能PerformanceNavigationTiming
'paint'
- 绘制性能(如 FP、FCP)。描述从 render tree 到绘画在屏幕上的一个个像素PerformancePaintTiming
'longtask'
- 长任务,阻塞 UI 进程超过 50ms 的任务PerformanceLongTaskTiming
'element'
- 特定元素的性能PerformanceElementTiming
buffered: true
- 含义:是否处理缓冲区中的历史性能条目
- 作用 :
- 当设置为
true
时,会立即返回在调用observe()
之前已经存在的性能条目 - 当设置为
false
时,只观察调用observe()
之后新产生的性能条目
- 当设置为
- 使用场景 :
- 如果你希望在代码初始化时就能获取到页面加载早期的性能数据,应该设为
true
- 如果只关心后续发生的性能事件,可以设为
false
- 如果你希望在代码初始化时就能获取到页面加载早期的性能数据,应该设为
initiatorType: fetch | xmlhttprequest
- 含义 :
PerformanceResourceTiming
对象中的一个属性,用于表示 资源是由谁/什么发起的请求(即资源的初始化器类型)。它可以帮助开发者分析页面资源加载的来源和依赖关系。 - 作用 :
- 筛选出通过 fetch 或 xmlhttprequest 发起的请求
- 使用场景 :
- 性能优化分析
- 可以统计哪些类型的资源加载最慢(例如
script
、css
、img
)。 - 检测是否有意外的
fetch
/xmlhttprequest
请求影响性能。
- 可以统计哪些类型的资源加载最慢(例如
- 错误监控
- 结合
responseStatus
(如我们代码所示),可以监控 AJAX/Fetch 请求是否失败(>= 400
)。
- 结合
- 性能优化分析
initiatorType
的可能值及含义
以下是常见的 initiatorType 值及其对应的含义:
值 | 说明 |
---|---|
"img" |
资源由 <img> 标签加载(例如 <img src="image.jpg"> ) |
"script" |
资源由 <script> 标签加载(例如 <script src="app.js"> ) |
"link" |
资源由 <link> 标签加载(例如 <link rel="stylesheet" href="style.css"> ) |
"css" |
资源由 CSS 规则加载(例如 @import "theme.css" 或 background: url(...) ) |
"xmlhttprequest" |
资源由 XMLHttpRequest 请求加载(AJAX) |
"fetch" |
资源由 fetch() 请求加载 |
"iframe" |
资源由 <iframe> 的 src 加载(例如 <iframe src="page.html"> ) |
"navigation" |
资源是页面本身(即 HTML 文档的加载) |
"audio" / "video" |
资源由 <audio> 或 <video> 标签加载 |
"beacon" |
资源由 navigator.sendBeacon() 发送 |
"other" |
其他未明确分类的请求 |
组合效果
{ type: 'resource', buffered: true }
表示:
- 观察所有资源加载的性能数据
- 立即获取已经存在的资源加载性能数据(而不仅仅是后续新发生的)
实际应用示例
javascript
// 这样设置可以获取到页面加载初期所有资源的性能数据
observer.observe({
type: 'resource',
buffered: true
});
// 如果只想观察后续发生的资源加载
observer.observe({
type: 'resource',
buffered: false
});
注意事项
buffered: true
可能会一次性返回大量历史数据,要注意处理性能- 不是所有性能条目类型都支持
buffered
选项 - 使用完毕后应调用
observer.disconnect()
停止观察