前端错误的分类 
系统异常 
系统异常比较少,相关可能为浏览器奔溃
网络异常 
- 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与样式异常
埋点 
数据上报可以在延伸扩展:即时,批量,主动等上报方式,可根据业务优先级来决定
代码埋点与数据上报

