Vue 异常捕获
app.config.errorHandler
用于为应用内抛出的未捕获错误指定一个全局处理函数。 错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.config.errorHandler = (err, instance, info) => {
console.log('errorHandler: ', err, instance, info);
// 处理错误,例如:报告给一个服务
};
对于Vue 的运行时警告,可以用app.config.warnHandle指定一个自定义处理函数。
errorCaptured
errorCaptured生命周期可以捕获后代组件传递的错误。
export default {
/**
* 钩子带有三个实参:
* 错误对象、发生错误的组件实例
* 以及一个包含错误来源信息的字符串。
* 此钩子可以返回 false 以阻止该错误继续向上传播。
*/
errorCaptured(err, instance, info) {
console.log('errorCaptured', err, instance, info);
return false;
}
};
错误传递规则
- 默认情况下,所有的错误都会被发送到应用级的
app.config.errorHandler(前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务。 - 如果组件的继承链或组件链上存在多个
errorCaptured钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。 - 如果
errorCaptured钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到app.config.errorHandler。 errorCaptured钩子可以通过返回false来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的errorCaptured钩子或app.config.errorHandler因这个错误而被调用。
React错误处理
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
window 全局错误捕获
window.onerror
当有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中打印
window.addEventListener('error')
与onerror的功能大体类似,不过事件回调函数传参只有一个保存所有错误信息的参数,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误: 当一项资源(如img或script)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过能被window.addEventListener在捕获阶段捕获。
window.addEventListener('error', function (event: ErrorEvent) {
console.log('addEventListener error', event);
},true);
- 通过
window.addEventListener来实现,网络异常可以在事件捕获的阶段捕捉到,addEventListener第三个参数设置为true - 代码必须放在文档载入之前
- 通过
e.srcElement来判断网络错误事件发生的元素
注意,由于安全限制,window.onerror、window.addEventListener('error')直接在控制台运行时错误不会被捕获。
Promise Catch
// 全局监听 Uncaught Promise Error。
window.addEventListener("unhandledrejection", function(e){
// e.preventDefault(); // 阻止异常向上抛出
console.log('捕获到异常:', e);
});
window.onunhandledrejection = function(e){
// e.preventDefault(); // 阻止异常向上抛出到全局,不在console中显示
console.log('捕获到异常:', e);
};
跨域文件错误
如果引用外链不同源的文件,外链不同源js文件报错,onerror只会提示Script error,无法精确到指定文件和行数,可以通过script标签的:
crossorigin="anonymous"
资源服务器也需要开启cors验证,并允许引用页面的域名访问
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Range
Access-Control-Allow-Origin:*
sourceMap
通常在生产环境下的代码是经过webpack打包后压缩混淆的代码,开启webpack的source-map,我们利用webpack打包后的生成的一份.map的脚本文件就可以让浏览器对错误位置进行追踪了。
开启source-map的缺陷是兼容性,目前只有Chrome浏览器和Firefox浏览器才对source-map支持。不过我们对这一类情况也有解决办法。可以使用引入npm库来支持source-map,可以参考mozilla/source-map。这个npm库既可以运行在客户端也可以运行在服务端,不过更为推荐的是在服务端使用Node.js对接收到的日志信息时使用source-map解析,以避免源代码的泄露造成风险,如下代码所示:
const express = require('express');
const fs = require('fs');
const router = express.Router();
const sourceMap = require('source-map');
const path = require('path');
const resolve = file => path.resolve(__dirname, file);
// 定义post接口
router.get('/error/', async function(req, res) {
// 获取前端传过来的报错对象
let error = JSON.parse(req.query.error);
let url = error.scriptURI; // 压缩文件路径
if (url) {
let fileUrl = url.slice(url.indexOf('client/')) + '.map'; // map文件路径
// 解析sourceMap
let consumer = await new sourceMap.SourceMapConsumer(fs.readFileSync(resolve('../' + fileUrl), 'utf8')); // 返回一个promise对象
// 解析原始报错数据
let result = consumer.originalPositionFor({
line: error.lineNo, // 压缩后的行号
column: error.columnNo // 压缩后的列号
});
console.log(result);
}
});
module.exports = router;
复制代码
数据上报方式
关闭或者最小化浏览器时回调
- beforeunload (关闭前确认弹窗) 部分移动端不触发
- unload 关闭时 部分移动端不触发
- pagehide 隐藏时 部分移动端不触发
延迟浏览器关闭或跳转的方法:
用户场景:当用户导航到另一个页面或者关闭页面,希望关闭或跳转前将数据完整发送。
-
创建一个img元素并设置它的src。
- 在src中创建埋点url,服务端通过get方式接收
- 原理:占用浏览器主线程,大多数浏览器会延迟卸载以加载图像。
-
创建几秒钟的无操作循环。
let currTime=new Date().valueOf())
while(new Date()>currTime+3000){}
复制代码
-
通过配置将XHR同步发送请求
- axios默认是异步的,浏览器可能会过早关闭或跳转导致取消发送
- 通过配置将XHR改为同步发送请求,但是可能会影响用户跳转的速度。
-
sendBeaconAPI 64KB限制 IE兼容性问题 post请求
- body大小64KB限制 IE兼容性问题 post请求
- 浏览器将 Beacon 请求排队让它在空闲的时候执行
- 浏览器进行了优化:可以将 Beacon 请求合并到其他请求上,一同处理, 尤其在移动环境下。
- 异步发送 不阻塞进程