Skip to content

前端错误的分类

系统异常

系统异常比较少,相关可能为浏览器奔溃

网络异常

  • 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,解决方案

  1. 在 script 标签增加 crossorigin="anonymous" 属性
  2. 设置 Access-Control-Allow-Origin: *

监控平台搭建

这个应该每个公司都有自己的监控平台,这里不列举过多。

如果数据量过大,架构需要对行为记录存储的设计与成本进行考虑。如容器存储与存放时间等

数据监控

  • PV:即页面浏览量或点击量
  • UV:指访问某个站点或点击某条新闻的不同IP地址的人数
  • 页面停留时长
  • 用户与数据来源
  • 触发行为

性能监控

  • 不同环境下机型与系统下的首屏加载时间
  • DNS、TCP、request、页面渲染、load、加载、白屏等耗时时间

异常监控

JavaScript与样式异常

埋点

数据上报可以在延伸扩展:即时,批量,主动等上报方式,可根据业务优先级来决定

代码埋点与数据上报

diagram1.jpg

Layout Switch

Adjust the layout style of VitePress to adapt to different reading needs and screens.

Expand all
The sidebar and content area occupy the entire width of the screen.
Expand sidebar with adjustable values
Expand sidebar width and add a new slider for user to choose and customize their desired width of the maximum width of sidebar can go, but the content area width will remain the same.
Expand all with adjustable values
Expand sidebar width and add a new slider for user to choose and customize their desired width of the maximum width of sidebar can go, but the content area width will remain the same.
Original width
The original layout width of VitePress

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.

Spotlight

Highlight the line where the mouse is currently hovering in the content to optimize for users who may have reading and focusing difficulties.

ONOn
Turn on Spotlight.
OFFOff
Turn off Spotlight.