前端错误的分类
系统异常
系统异常比较少,相关可能为浏览器奔溃
网络异常
XMLHttpRequest
请求异常Fetch
请求异常- 静态资源加载异常
应用异常
- Error:错误的基类,其他错误都继承自该类型。
- EvalError : 与
eval()
有关的错误。 - RangeError : 表示这个值不在允许值集或范围内。
- ReferenceError : 表示发现一个无效的引用。
- SyntaxError : 表示发生了解析错误。
- TypeError :当其它类型错误都不符合时,TypeError 用于指示一个不成功的操作。
- URIError :表示用于处理 URI 的函数(encodeURI 或 decodeURl)使用方式与其定义的不兼容。
- 异常参考:Top 10 JavaScript errors from 1000+ projects
异常捕获
try/catch/finally
javascript
try {
var a = 1
var b = a + c
} catch (error) {
// 捕获处理
console.log(error) // ReferenceError: c is not defined
logger.error('catch', error)
} finally {
throw new Error('error')
}
思考: 如果 catch 块和 finally 块都抛出异常,catch 块的异常是否能抛出? 当该 finally
块引发异常时,它将有效地隐藏从该块引发的异常,并将 catch
最终引发该异常。因此,重要的是要么在捕获时记录异常,要么确保 finally
块本身不会引发异常。
window.onerror
请注意
window.error
无法捕获静态资源异常和 JS 代码错误。
javascript
/**
* @param {String} message 错误信息
* @param {String} source 错误文件路径
* @param {Number} lineno 错误行号
* @param {Number} colno 错误列号
* @param {Object} error Error对象(对象)
*/
window.onerror = function (message, source, lineno, colno, error) {
console.log(`捕获到异常:${(message, source, lineno, colno, error)}`)
logger.error('oneror', JSON.stringify({ message, source, lineno, colno, error }))
}
静态资源加载 异常
html
<script>
function errorHandler(error) {
console.log(`捕获到静态资源加载异常: ${error}`)
logger.error('onerror', JSON.stringify(error))
}
</script>
<script src="http://cdn.xxx.com/js/test.js" onerror="errorHandler(this)"></script>
<link
rel="stylesheet"
href="http://cdn.xxx.com/styles/index.css"
onerror="errorHandler(this)"
/>
Promise 异常
javascript
window.addEventListener('unhandledrejection', (event) => {
console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`)
logger.error('promise', JSON.stringify(event))
event.preventDefault()
})
// 或
window.onunhandledrejection = (event) => {
console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`)
logger.error('promise', JSON.stringify(event))
event.preventDefault()
}
Vue
javascript
/**
* @name Vue 异常上报
* @param {{message,name,script,line,column,stack}} err error 对象
* @param {String} vm 抛出异常的 Vue 实例对象
* @param {String} info Vue 特定的错误信息,比如错误所在的生命周期钩子
*/
Vue.config.errorHandler = (err, vm, info) => {
console.log('vue errorHandler', { err, vm, info })
logger.error('vue error', JSON.stringify({ err, vm, info }))
}
React
javascript
import React from 'react'
import { Modal } from 'antd'
import './styles.scss'
export default class ErrorBoundary extends React.Component {
state = { hasError: false, error: null, tipText: null }
componentDidCatch(error, info) {
console.error(error, info)
logger.error('react error', JSON.stringify({ error, info }))
if (
error &&
(error.toString().indexOf('ChunkLoadError') === 0 ||
error.toString().indexOf('Error: Loading CSS ') === 0)
) {
// 监测到Webpack异步模块加载失败
Modal.confirm({
title: '检测到网站可能有更新,需要刷新页面',
okText: '刷新',
cancelText: '关闭',
maskClosable: false,
onOk: () => {
location.reload()
},
})
this.setState({ hasError: true, tipText: '网站可能有更新,请刷新页面' })
} else {
this.setState({ hasError: true, error, tipText: null })
}
}
render() {
if (this.state.tipText) {
return (
<div className='page-error page-standard'>
<h2 className='title'>{this.state.tipText}</h2>
</div>
)
} else if (this.state.hasError) {
return (
<div className='page-error page-standard'>
<h2 className='title'>抱歉,页面出错</h2>
<h5 className='tip'>请尝试刷新页面,或联系技术人员,以下是错误信息:</h5>
<div className='error-message'>
{this.state.error ? this.state.error.toString() : '错误:未知错误'}
</div>
</div>
)
}
return this.props.children
}
}
// 使用案例:启动入口页面包裹
import React from 'react'
import { render } from 'react-dom'
import ErrorBoundary from 'components/ErrorBoundary'
const appRoot = document.getElementById('root')
appRoot.setAttribute('notranslate', true)
render(<ErrorBoundary>{/* code... */}</ErrorBoundary>, appRoot)
延伸: 跨域的 js 运行错误可以捕获吗,错误提示什么,应该怎么处理? 跨域之后 window.onerror
是无法捕获异常信息的,所以统一返回 Script error
,解决方案
- 在 script 标签增加 crossorigin="anonymous" 属性
- 设置 Access-Control-Allow-Origin: *
监控平台搭建
这个应该每个公司都有自己的监控平台,这里不列举过多。
如果数据量过大,架构需要对行为记录存储的设计与成本进行考虑。如容器存储与存放时间等
数据监控
- PV:即页面浏览量或点击量
- UV:指访问某个站点或点击某条新闻的不同IP地址的人数
- 页面停留时长
- 用户与数据来源
- 触发行为
性能监控
- 不同环境下机型与系统下的首屏加载时间
- DNS、TCP、request、页面渲染、load、加载、白屏等耗时时间
异常监控
JavaScript与样式异常
埋点
数据上报可以在延伸扩展:即时,批量,主动等上报方式,可根据业务优先级来决定
代码埋点与数据上报