2022-11-29【监控】前端性能监控

onerror

window.onerror 是一个全局变量,默认值为 null。当有 js 运行时错误触发时,window 会触发 error 事件,并执行 window.onerror()。onerror 可以接受多个参数。

1
2
3
4
5
6
7
8
9
10
11
window.onerror = function(message, source, lineno, colno, error) { ... }

函数参数:

* message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。
* source:发生错误的脚本URL(字符串)
* lineno:发生错误的行号(数字)
* colno:发生错误的列号(数字)
* error:Error对象

若该函数返回true,则阻止执行默认事件处理函数,如异常信息不会在console中打印。没有返回值或者返回值为false的时候,异常信息会在console中打印

addEventListener(‘error’)

监听 js 运行时错误事件,会比 window.onerror 先触发,与 onerror 的功能大体类似,不过事件回调函数传参只有一个保存所有错误信息的参数,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误

1
2
3
4
5
6
7
8
window.onerror = function (message, source, lineno, colno, error) {
console.warn("onerror============ %s", error);
return true;
};
window.addEventListener("error", function (event) {
console.warn("onerror============ %s", event);
return true;
});

vue 异常 钩子

1
2
3
4
Vue.config.errorHandler = function (err, vm, info) {
console.log(`Error: ${err.toString()}\nInfo: ${info}`);
aegis.error(`Error: ${err.toString()}\nInfo: ${info}`);
};

vue 路由 异常钩子

1
2
3
4
5
6
7
8
9
10
router.onError((error) => {
const jsPattern = /Loading chunk (\S)+ failed/g;
const cssPattern = /Loading CSS chunk (\S)+ failed/g;
const isChunkLoadFailed = error.message.match(jsPattern || cssPattern);
const targetPath = router.history.pending.fullPath;
if (isChunkLoadFailed) {
localStorage.setItem("targetPath", targetPath);
window.location.reload();
}
});
  • 如果想通过 onerror 函数收集不同域的 js 错误,我们需要做两件事:
  1. 相关的 js 文件上加上 Access-Control-Allow-Origin:*的 response header
  2. 引用相关的 js 文件时加上 crossorigin 属性
1
<script type="text/javascript" src="http://b.com/b.js" crossorigin></script>

异常上报工具

系统发版,用户跳转页面 404 问题

通过 window.addEventListener 监听资源加载报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// index.html head顶部
window.addEventListener(
"error",
(e) => {
// 防止刷新过后资源依然404导致页面无限刷新, 在router.afterEach里删除
if (sessionStorage.staticReload) {
return;
}
let localName = e.target.localName;
if (localName === "script" || localName === "link") {
// 只刷新一次 用 staticReload 标记一下
window.sessionStorage.setItem("staticReload", "1");
// reload里一定要加 true
window.location.reload(true);
}
// 这个true一定要加,否则监听不到
},
true
);

// 或
window.addEventListener(
"error",
function (event) {
try {
const target = event.target || event.srcElement;
if (
target instanceof HTMLElement &&
["LINK", "SCRIPT", "IMG"].indexOf(target.nodeName) !== -1
) {
// 下载资源失败
// @ts-ignore
const src = target.src || target.href;
if (window.location.href.indexOf(src) !== 0) {
reportResourceFail({
detail: src,
});
}
}
} catch (err) {}
},
true
);
1
2
3
4
5
// router.js
// 跳转成功,说明css js资源加载成功,则去掉 staticReload 标记
router.afterEach(() => {
sessionStorage.removeItem("staticReload");
});

注意点

  1. window.addEventListener 的第三个参数 一定要是 true,表示用事件捕获。
  2. window.location.reload(true) 里一定要用 true, 表示直接从服务器取资源。
  3. 加一个标记,只刷新一次就够了,因为如果是因为某个资源本身不存在导致刷新完又 404,会让页面无限刷新。我们解决的只是能保证刷新页面就能获取到的正常打包资源。

结语

这样一旦用户停留页面的时间正好跨越了发版时间,资源加载 404 的情况下,会直接在当前页面强行刷新一下,然后就能正常使用了。


2022-11-29【监控】前端性能监控
https://zhangyingxuan.github.io/2022-11-29-【监控】异常上报及监控/
作者
blowsysun
许可协议