前言不知道大家有没有这样的经验。调试本地开发项目一切正常。一旦上线,就会出现各种奇怪的问题。原因有很多种,环境变量不同,宿主机环境不同,接口返回的数据格式不同,代码逻辑问题等等。有的错误在开发测试过程中可以发现,有的错误要上线才能发现。这个时候等待用户反馈就太被动了。因此,一个收集和捕获代码错误的监控系统就显得尤为重要。.本系列文章的目的不是让大家马上搭建一个前端代码监控系统,而是介绍浏览器环境下常见的代码错误捕获方式,以及制作前端采集插件通过我们熟悉的方法对代码错误进行分析,最后再简单介绍一下收集错误信息后如何快速定位错误。常见的错误类型在开始我们的话题之前,我们首先需要了解常见的错误类型。代码编写错误是最容易消除的。我们往往只需要借助编辑器的自动检测,或者eslint、tslint等第三方工具,在开发阶段就可以解决。当然,如果有同学使用文本编辑器写代码,请下载一个vscode,在接受我的膝盖的同时,体验更好的代码编写环境。functionfoo()contbar="string"代码执行错误这个可以说是平时开发中遇到最多的了,比如下面这个例子,相信很多同学在使用vue或者react的时候都是通过接口获取列表数据的,如果后台同学空数据不返回[],而是返回null或者undefined,前端显示就会有问题。也有同学会说ts方法好,接口定义香(但实际上这并不能解决接口返回不一致的问题),如果遇到同事的类型定义都是any的情况。..undefined.map((item)=>console.log(item));其他错误类型,比如使用了一个未定义的变量,但其实这种错误在开发阶段是可以避免的。控制台日志(你好世界);异步代码reject不知道大家在使用promises的时候有没有catchreject的习惯。我相信除了我以外大部分人都有(违背自己的意愿),那么对于不主动抓取的reject,我们应该如何抓取和处理呢?constpromise=()=>newPromise((resolve,reject)=>{setTimeout(()=>{reject(newError("promisereject"));},1000);});//未使用的catch捕获错误promise().then(console.log);资源加载错误资源加载错误也是个秃头问题,如果只是image,还好,如果是重要的js,css资源加载错误。..捕获代码错误NextAs就抓取我们说的这几种报错,可能有同学会说,哎呀,我平时用的是vuereact,不可能这么简单的场景出错。嘘,等等,框架以那些简单的方式抛出错误。另外,下面的内容有详细的代码和注释。小伙伴们可以直接clone代码本地调试。详情参见项目分支的README.md。示例地址gitclone-bdemo/catch-code-errorhttps://github.com/jsjzh/tiny-codes.git代码执行错误在写代码的时候,如果遇到可能执行错误的逻辑,我们一般会使用trycatch,wrap,然后单独做,但是对于一些意想不到的错误,比如上面接口的返回;如果异步执行的代码出现错误,则trycatch无法捕捉到错误。这时候,window.onerror就派上用场了。我们可以这样理解,trycatch用来捕捉可预见的错误,window.onerror用来捕捉意外的错误,这就是catch-all策略。/***message{String}:错误信息*fileName{String}:发生错误的文件*col{Number}:错误代码行*row{Number}:错误代码列*error{Error}:错误对象*/window.onerror=(message,fileName,col,row,error)=>{console.log("message:",message);console.log("文件名:",文件名);console.log("col:",col);console.log("行:",行);console.log("error.name:",error.name);console.log("error.message:",error.message);控制台.log(“错误:”,错误);};主动尝试捕获以下代码。我们主动trycatcherror,也就是说我们知道try的逻辑可能有错误,我们已经在catch中处理了。其实这种错误情况已经可以不用上报了,因为我们已经做了相应的错误处理。ps:如果还需要报错,可以在catch中加入throwerror逻辑,这样就可以将错误抛出并被window.onerror捕获。//同步执行代码,通过trycatchtry{console.log(helloWorld);}catch(error){//在这里做一些自定义处理//...//如果做自定义处理,还需要报错,加上throwerror//throwerror;}意外报错以下几种情况,我们没想到会报错,会直接被window.onerror捕获。ps:这里process对象是在浏览器环境下访问的,只有在node环境下才是全局变量。//没有使用trycatch来捕获错误//说明我们没想到这段代码会出错console.log(process);异步代码执行错误如果异步逻辑代码中有代码执行错误,我们不能直接通过trycatch捕获,此时有两种方式,第一种是将异步执行的函数用trycatch包裹起来,我们将不举例,第二种方式是通过window.onerror来捕获错误。constpromise=()=>newPromise(()=>{setTimeout(()=>{console.log(helloWorld);},1000);});try{promise().then();}catch(error){//无法捕??获错误errorconsole.log("can'tcatcherror");}可以看到上图,错误被window.onerror捕获了。异步代码reject在说异步代码reject之前,我们必须有个共识,promise的catch是无法捕获代码执行错误的,只有通过reject()抛出的异常才会被promise.catch捕获。你是什??么意思?请参阅下面的代码。constpromise=()=>newPromise(()=>{setTimeout(()=>{console.log(helloWorld);},1000);});promise().then()//不能捕获helloWorlderror.catch(()=>{console.log("无法捕获错误");});什么错误可以捕获promise捕获?答案是reject抛出的错误,如下。constpromise2=()=>newPromise((resolve,reject)=>{setTimeout(()=>{//通过reject抛出错误reject(newError("promisereject"));},1000);});promise2().then().catch((error)=>{//可以捕获错误console.log("error.name:",error.name);console.log("error.message:",error.message);console.log("error:",错误);});以上内容只是为了大家达成共识,接下来说说如何捕获uncatchrejection,通过window.onunhandledrejection。ps:需要提醒一点。对于抛出的自定义错误,尽量使用newError(...),这样我们不仅可以得到完整的错误信息,还可以得到文件名、错误行和错误列。这就是所谓的堆栈信息。ps2:如果rejection被promise.catch捕获,则不会被window.onunhandledrejection捕获。如果还想报这个错,可以使用throwerror,会被window.onerror捕获。window.onunhandledrejection=(promiseRejectEvent)=>{//错误对象,由reject(newError())生成constreason=promiseRejectEvent.reason;如果(原因){console.log(“reason.name:”,reason.name);console.log("reason.message:",reason.message);console.log("reason.stack:",reason.stack);}};constpromise2=()=>newPromise((resolve,reject)=>{setTimeout(()=>{//通过reject抛出错误reject(newError("promisereject"));},1000);});//没有使用catch,错误会被window.onunhandledrejection捕获promise2().then();资源加载错误对于资源加载错误,无论是js、css、image,我们都可以通过window.addEventListener("error")来监听和捕获错误。但是使用background-image:url(./error.png)导致的资源加载错误和newImage().src="./error.png"导致的资源加载错误无法通过该方法捕获。ps:如果有谁知道如何捕获这些加载错误,请告诉我,我自测成功后会更新文章。window.addEventListener("error",(sourceErrorEvent)=>{consttargetElement=sourceErrorEvent.target||sourceErrorEvent.srcElement;consturl=targetElement.src||targetElement.href;console.log("sourceError:",url);},true);
