onerror
window.onerror是一个全局变量,默认值为null。当有js运行时错误触发时,window会触发error事件,并执行window.onerror()。onerror可以接受多个参数。
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的功能大体类似,不过事件回调函数传参只有一个保存所有错误信息的参数,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误
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 异常 钩子
Vue.config.errorHandler = function (err, vm, info) {
console.log(`Error: ${err.toString()}\nInfo: ${info}`);
aegis.error(`Error: ${err.toString()}\nInfo: ${info}`);
};
vue 路由 异常钩子
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错误,我们需要做两件事:
- 相关的js文件上加上Access-Control-Allow-Origin:*的response header
- 引用相关的js文件时加上crossorigin属性
<script type="text/javascript" src="http://b.com/b.js" crossorigin></script>
异常上报工具
系统发版,用户跳转页面 404问题
通过 window.addEventListener 监听资源加载报错
// 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);
// router.js
// 跳转成功,说明css js资源加载成功,则去掉 staticReload 标记
router.afterEach(() => {
sessionStorage.removeItem('staticReload')
})
```
注意点
- window.addEventListener 的第三个参数 一定要是 true,表示用事件捕获。
- window.location.reload(true) 里一定要用 true, 表示直接从服务器取资源。
- 加一个标记,只刷新一次就够了,因为如果是因为某个资源本身不存在导致刷新完又404,会让页面无限刷新。我们解决的只是能保证刷新页面就能获取到的正常打包资源。
###结语 这样一旦用户停留页面的时间正好跨越了发版时间,资源加载404的情况下,会直接在当前页面强行刷新一下,然后就能正常使用了。