不想使用三方监控平台监控白屏报错?试试手动写一个白屏报错组件吧

先看看最终实现的监控效果

能够准确找到文件位置,报错文件名,具体的索引代码。

第一步:开启配置

以vite打包为例:需要开启打包配置: 在vite的build配置加上:

js 复制代码
    sourcemap: true,
    terserOptions: {
      sourceMap: true,
      compress: {
        passes: 2,
      },
    },

同时需要避免vite的插件影响到sourceMap的定位

第二步:配置sourceMap插件和stacktrace-js

sourceMap

首先配置前端source-map,用于解析source-map文件,通过行数和列数获取目标错误代码内容。 全局index.html文件加入以下内容:

js 复制代码
    <script defer>
      function loadScript(url, callback) {
        var script = document.createElement('script')
        script.type = 'text/javascript'
        if (script.readyState) {
          script.onreadystatechange = function () {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
              script.onreadystatechange = null
              callback()
            }
          }
        } else {
          script.onload = function () {
            callback()
          }
        }
        script.src = url
        script.defer = true
        document.getElementsByTagName('head')[0].appendChild(script)
      }
      loadScript('https://unpkg.com/[email protected]/dist/source-map.js', function () {
        window.sourceMap.SourceMapConsumer.initialize({
          'lib/mappings.wasm': 'https://unpkg.com/[email protected]/lib/mappings.wasm',
        })
      })
    </script>

这样我们全局就会有一个sourceMap对象:

stacktrace-js

js 复制代码
pnpm add stacktrace-js -D

第三步:如何捕获错误

针对React,在类组件中提供了两个Error相关的钩子:componentDidCatch和getDerivedStateFromError, 我们可以封装一个高阶组件,在componentDidCatch捕获Error对象和Error详细信息:

js 复制代码
async componentDidCatch(error: any, errorInfo: any){
}

接下来我们从这两个对象入手,获取报错的具体位置和代码:

首先我们获取到报错的具体信息:

error.message 这是一个字符串类型,反应报错的内容

使用stacktrace-js获取报错文件和报错的位置信息

js 复制代码
import StackTrace, { StackFrame } from 'stacktrace-js'

const getErrorPositionMap = (error:Error)=>{
    const fileNameMap: Record<string, { line: number; column: number }> = {}
    const stackFrames = await StackTrace.fromError(error, {
      filter: (stackFrame: StackFrame) => !stackFrame?.fileName?.includes('/node_modules/'),
      // @ts-ignore
      ajax: (url) => fetch(`${url}?${Date.now()}`).then((res) => res.text()), //cdn可能会有获取不到的情况,保证文件是最新的
    })
    stackFrames.forEach((item) => {
      const { fileName, lineNumber = 0, columnNumber = 0 } = item
      if (fileName) {
        fileNameMap[fileName] = { line: lineNumber, column: columnNumber }
      }
    })
}

使用sourceMap获取报错的内容信息

通过位置信息,我们可以获取到报错的具体代码片段,通过递归调用,并且排除node_modules文件的报错,我们可以得到我们代码中的具体错误位置,我这里只获取了最近的一个文件报错位置,如果想把错误链都展示出来,可以对下面代码进行优化

js 复制代码
const getSourceCodeFromErrorPosition = async (
  componentStack: string,
  fileNameMap: Record<string, { line: number; column: number }>
) => {
  // @ts-ignore
  const urlList = Array.from(
    componentStack.matchAll('(http.*?\\.js):([0-9]+):([0-9]+)')
  ) as string[][]
  const result = {
    source: '',
    specificPosition: '',
  }

  let lastFilePath = ''
  let consumer: any
  const loop = async (index: number) => {
    const [, filePath, lineNumber, columnNumber] = urlList[index]
    if (filePath !== lastFilePath) {
      const rawSourceMap = await fetch(
        `${filePath.replace(/\.js$/, '.js.map')}?${Date.now()}`
      ).then((res) => res.json())
      consumer?.destroy()
      consumer = await new window.sourceMap.SourceMapConsumer(rawSourceMap)
    }
    lastFilePath = filePath
    const originalPosition = consumer.originalPositionFor({
      line: +lineNumber,
      column: +columnNumber,
    })

    if (
      (originalPosition.source?.includes('/node_modules/') ||
        !fileNameMap[originalPosition.source]) &&
      index !== urlList.length - 1
    ) {
      await loop(index + 1)
      return
    }
    const sourceIndex = consumer.sources.findIndex(
      (item: string) => item === originalPosition.source
    )
    const sourceContent = consumer?.sourcesContent[sourceIndex]
    const contentRowArr = sourceContent?.split('\n')
    const positionInfo = fileNameMap[originalPosition.source] || originalPosition
    result.source = `${originalPosition.source} ${positionInfo.line}:${positionInfo.column}`
    result.specificPosition =
      contentRowArr?.[(fileNameMap[originalPosition.source]?.line || originalPosition.line) - 1]

    consumer?.destroy()
  }

  await loop(0)

  return result
}

函数使用: getSourceCodeFromErrorPosition(errorInfo.componentStack,getErrorPositionMap(error)), 这样我们可以获取到一个 {

source: string;

specificPosition: string;

} 对象,source是具体的报错代码片段,specificPosition是位置信息。

相关推荐
Net蚂蚁代码1 小时前
Angular入门的环境准备步骤工作
前端·javascript·angular.js
小着3 小时前
vue项目页面最底部出现乱码
前端·javascript·vue.js·前端框架
lichenyang4536 小时前
React ajax中的跨域以及代理服务器
前端·react.js·ajax
呆呆的小草6 小时前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
WHOAMI_老猫6 小时前
xss注入遇到转义,html编码绕过了解一哈
javascript·web安全·渗透测试·xss·漏洞原理
一 乐7 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
testleaf7 小时前
前端面经整理【1】
前端·面试
好了来看下一题7 小时前
使用 React+Vite+Electron 搭建桌面应用
前端·react.js·electron
啃火龙果的兔子7 小时前
前端八股文-react篇
前端·react.js·前端框架
小前端大牛马7 小时前
react中hook和高阶组件的选型
前端·javascript·vue.js