配置目录结构
错误监听:可以提前发现前端的错误,并且找到对应的位置进行修改。因为等等环境因素可能导致不同的问题,这些问题难以发现,影响用户体验。
性能监听:可以及时发现问题,比如下载的js文件、image时间过长,白屏、渲染时间长等等问题。做对应的优化处理。
在utils/monitor新建两个js文件,分别是errorLog,js全局错误监听,performanceLog.js全局性能监听
在main.js中引入
这里是在自己封装的请求拦截器里面,监听请求错误
代码
errorLog.js
注释很详细,包含所有前端错误监听(请求错误在自己封装的请求拦截器response中收集)
主要用到
1、window.addEventListener(‘error’,error=>{}) 静态资源加载错误 或者 JS错误(不会捕获promise)
2、window.addEventListener(‘unhandledrejection’,error=>{}) 监听promise错误
3、app.config.errorHandler=error =>{} vue渲染或者vue处理错误(react用法自查)
监听在页面刷新或关闭前,将错误发送到服务器(我这里加了个,当50条错误的时候先上传)
/**
* 前端错误主要有四种: JS错误、静态资源错误、vue逻辑错误、请求错误
*
* 三种方式上传数据
* 参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/sendBeacon
* 1、axios
* 2、image:get请求,参数大小受限,不同浏览器不一样(2k-8k)
* let image = new Image()
* image.src = `https://www.xx.com?log=${encodeURI(JSON.stringify(data))}`
* 3、sendBeacon:(推荐)post异步请求可以保证数据有效送达,且不会阻塞页面的卸载或加载
*
*/
import router from '@/router/index'
const SendUrl = process.env.VUE_APP_API_ERROR // https:www.xx.com
const monitor = { // 监控
errors: [] // 错误
}
// 错误代码
const errCode = new Map([
// 本地系统错误
['E1001', '系统未知错误'],
['E1002', 'vue逻辑错误'],
['E1003', 'JavaScript错误'],
['E1004', '静态资源加载错误'],
['E1005', '请求错误'],
['E1006', 'Promise错误']
])
/**
* 开启监听
* @param {any} app
*/
export const errorLog = (app) => {
/**
* vue渲染或者vue处理错误
* @param {object} error Error对象
* @param {object} vm Vue 应用本身
* @param {string} info Vue 特有的字符串
*/
app.config.errorHandler = (error, vm, info) => {
console.error('监测到E1002错误');
console.error(error.filename);
errorHandler('E1002', error.name + ':' + error.message)
}
/**
* 静态资源加载错误 或者 JS错误(不会捕获promise)
* @param {string} message 错误信息
* @param {string} source 发生错误的脚本URL
* @param {number} lineno 发生错误的行号
* @param {number} colon 发生错误的列号
* @param {object} error Error对象
*/
window.addEventListener('error', (error, message, source, lineno, colon) => {
// 区分是否是js错误
if (error.message) {
console.error('监测到E1003错误');
errorHandler('E1003', error.message, error.filename)
} else {
console.error('监测到E1004错误');
errorHandler('E1004', error.target.currentSrc, error.filename)
}
}, true)
/**
* 监听 promise 错误 缺点是获取不到行数数据
*
*/
window.addEventListener("unhandledrejection", error => {
console.error('监测到E1006错误');
errorHandler('E1006', error.message, error.filename)
}, true)
/**
* 在即将离开当前页面(刷新或关闭)时执行
*/
window.addEventListener('beforeunload',() => {
saveLog()
})
}
/**
* 错误处理函数
* 加一层判断,如果数据大于50条,将错误信息先上传
* @param {string} code 错误码
* @param {string} msg 错误信息 - 请求错误:error错误信息 + url请求地址 + param参数
* @param {string} file 文件
*/
export const errorHandler = (code, msg, file = 'null') => {
// 这里处理监听到的错误
// 保存错误信息
monitor.errors.push({
code: code,
type: errCode.get(`${code}`),
msg: msg,
router: router.app._route.fullPath,
file: file,
createTime: new Date().toLocaleString()
})
if(monitor.errors.length>=50){
saveLog()
}
}
/**
* 数据上报
*/
const saveLog = () => {
navigator.sendBeacon(SendUrl, JSON.stringify(monitor.errors))
monitor.errors = []
}
performanceLog.js
在浏览器渲染完成时,上传一次数据。
/**
* 加载资源:css、img、link、script等等
* 性能:白屏时间、dom渲染耗时、页面加载耗时等等
*
* 在页面加载完成之后上传 监控数据 数组
*/
import router from '@/router/index'
const SendUrl = process.env.VUE_APP_API_PERFORMANCE // https:www.xx.com
const monitor = { // 监控
performance: null, // 性能
resources: [], // 资源
}
export const performanceLog = ()=>{
/**
* 在页面加载完后上报性能数据
*/
window.addEventListener('load',()=>{
if(window.requestIdleCallback) { // 如果浏览器支持这个方法,利用这个方法采集页面性能数据
window.requestIdleCallback(() => {
monitor.performance = getPerformance()
monitor.resources = getResources()
savePerformance()
})
} else {
setTimeout(function() {
monitor.performance = getPerformance()
monitor.resources = getResources()
savePerformance()
}, 0)
}
})
}
/**
* 获取页面加载的时间性能信息
*/
function getPerformance() {
if (!window.performance) return
const timing = window.performance.timing
return {
whiteScreen: timing.domLoading - timing.navigationStart, // 白屏时间
redirect: timing.redirectEnd - timing.redirectStart, // 重定向耗时
dom: timing.domComplete - timing.domLoading, // dom渲染耗时
load: timing.loadEventEnd - timing.navigationStart, // 页面加载耗时
unload: timing.unloadEventEnd - timing.unloadEventStart, // 页面卸载耗时
request: timing.responseEnd - timing.requestStart, // 请求耗时
time: new Date().toLocaleString(), // 获取性能信息时当前时间
router: router.app._route.fullPath
}
}
/**
* 获取页面资源加载的时间性能信息
*/
function getResources() {
if (!window.performance) return
const data = window.performance.getEntriesByType('resource')
const resources = {
css: [],
img: [],
script: [],
xmlhttprequest: [],
link: [],
fetch: [],
iframe: [],
other: [],
time: new Date().toLocaleString()
}
data.forEach(item => {
let key = resources[item.initiatorType]?item.initiatorType:'other'
resources[key].push({
name: item.name, // 资源的名称
duration: item.duration.toFixed(2), // 资源加载耗时
size: item.transferSize, // 资源大小
protocol: item.nextHopProtocol, // 资源所用协议
})
})
return resources
}
function savePerformance(){
navigator.sendBeacon(VUE_APP_API_PERFORMANCE, JSON.stringify(monitor))
}