admin管理员组文章数量:1516870
前端老鸟血泪史:搞不定报错?这套错误处理策略让你少加三天班!
开篇先唠两句
别划走,我知道你刚被控制台那一堆红字搞崩了心态。那种看着满屏飘红的绝望,我懂——上周三凌晨两点,我盯着生产环境那个
Cannot read properties of undefined
的错误,手边的咖啡已经凉透,脑子里只有一个念头:这破代码到底是谁写的?哦,是三个月前的我自己。
咱们今天不整那些虚头八脑的理论,就聊聊怎么让那些该死的
Uncaught TypeError
闭嘴,顺便让你的代码健壮得像穿了防弹衣。说实话,错误处理这事儿吧,新手觉得麻烦,老鸟觉得保命。我见过太多项目,功能做得花里胡哨,一报错直接白屏,用户体验瞬间归零,产品经理的脸色比控制台还黑。
所以啊,这篇文章就是把我这些年踩过的坑、流过的泪、加过的班,统统打包成一份"防猝死指南"。你照着做,不一定能让你代码零 bug,但至少能让你睡个安稳觉。
到底啥是前端错误处理
简单说就是给代码买个保险。浏览器不会惯着你,用户更不会。一旦脚本报错,页面白屏或者按钮点了没反应,产品经理能顺着网线过来打你——这话真不是夸张,我亲眼见过隔壁组的老哥因为线上白屏十分钟,被拉去开了三小时的复盘会。
前端这地方,出错的姿势简直千奇百怪。你以为只是自己写的业务逻辑会崩?太天真了。网络抖一下,接口返回个
null
,用户手贱按了 F12 删掉某个 DOM 元素,甚至浏览器插件瞎注入脚本,都能让你的页面当场去世。所以咱们得把全局错误、资源加载失败、异步 Promise rejection 这些"定时炸弹"都认全了,别等炸了才想起来拆弹。
先说说全局错误。浏览器提供了一个
window.onerror
这个老古董,虽然 API 设计得有点反人类,但确实是最后一道防线。然后是那些 Promise 里没 catch 住的 rejection,现在满世界都是 async/await,一个 await 忘了包 try-catch,控制台就给你飘红。还有资源加载失败——图片挂了、CSS 丢了、JS 文件 404 了,这些都不会冒泡到 window.onerror,得用
window.addEventListener('error', ...)
专门监听。
最阴间的是那种第三方脚本搞出来的错。你接了个统计 SDK,或者埋了个广告位,他们代码一崩,你的页面跟着陪葬,找谁说理去?所以错误处理不是可选项,是刚需,是底线,是让你能安心下班的护身符。
扒开底层看看怎么抓错
光知道有错不行,得知道怎么逮住它。这节咱们把几个核心 API 和机制掰开了揉碎了讲,代码管够,注释写满,复制粘贴就能用。
window.onerror 这老伙计
说实话,
window.onerror
的 API 设计得挺离谱的,参数顺序跟别的 Web API 完全不是一个路数,但谁让人家辈分高呢?这玩意儿能捕获大部分同步错误,是全局监控的基石。
// 基础版:先能跑起来再说
window.onerror=function(message, source, lineno, colno, error){
console.log('抓到一个错误:',{
message,// 错误信息字符串,比如 "Uncaught TypeError: xxx is not a function"
source,// 出错的文件 URL
lineno,// 行号
colno,// 列号
error // 错误对象,里面堆栈信息最值钱});// 返回 true 可以阻止错误冒泡到控制台,但生产环境建议别瞎阻止,不然调试更难returnfalse;};// 进阶版:带点实际功能的
window.onerror=function(msg, url, line, col, error){// 过滤掉一些无关痛痒的扩展插件错误,不然日志里全是垃圾if(url && url.includes('chrome-extension')){returntrue;// 这种错误直接吞了,爱谁谁}// 构造错误报告对象const errorInfo ={type:'javascript',message: msg,filename: url,position:`${line}:${col}`,stack: error?.stack ||'no stack',// 顺便捞点环境信息,排查问题时候救命用userAgent: navigator.userAgent,timestamp:newDate().toISOString(),// 如果用了性能监控,可以把当前页面加载时间也带上timing: performance?.timing ?{domReady: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,loadTime: performance.timing.loadEventEnd - performance.timing.navigationStart
}:null};// 发送到监控系统,这里用 console 模拟一下
console.error('[全局错误捕获]', errorInfo);// 上报到服务器reportError(errorInfo).catch(e=>{// 上报失败也得记一下,别死循环
console.warn('错误上报失败:', e);});returnfalse;// 让错误继续抛出来,方便开发时看到};// 模拟上报函数asyncfunctionreportError(info){// 实际项目中这里换成你的上报接口// 比如 Sentry.captureException 或者自研的日志服务awaitfetch('/api/log/error',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(info),// 错误上报要用 keepalive,防止页面关闭时请求被掐断keepalive:true});}
注意几个坑:第一,
window.onerror
捕获不到 Promise 的错误,那是另一个故事;第二,跨域的脚本错误,如果不配
crossorigin
属性,你拿到的信息只有
Script error
,跟没抓一样;第三,返回值
true
能阻止默认处理(就是控制台不飘红了),但生产环境建议别乱用,不然真出问题了你连调试信息都看不到。
unhandledrejection 专门治各种不服
现在谁还不用 Promise 或者 async/await?这玩意儿用爽了,错误处理也更容易翻车。一个 async 函数里 await 了七八个接口,中间哪个抛异常没包住,控制台就给你来个
Uncaught (in promise) Error
。
// 基础监听
window.addEventListener('unhandledrejection',function(event){// event.reason 就是 rejected 的值,可能是 Error 对象,也可能是个字符串,甚至是个 undefined
console.error('有 Promise 没被 catch:', event.reason);// 阻止默认行为(控制台飘红),看情况决定要不要阻止// event.preventDefault();});// 生产环境实用版本
window.addEventListener('unhandledrejection',function(event){const reason = event.reason;// 构造错误信息,得兼容各种奇奇怪怪的 rejection 值let message ='Unhandled Promise Rejection';let stack ='';if(reason instanceofError){
message = reason.message;
stack = reason.stack;}elseif(typeof reason ==='string'){
message = reason;}else{// 有些人喜欢 reject 个对象,比如 reject({code: 500, msg: 'error'})try{
message =JSON.stringify(reason);}catch(e){
message =String(reason);}}const errorInfo ={type:'unhandledrejection',message: message,stack: stack,// 如果能拿到触发错误的源头,记下来source: event.target?.src ||'unknown',timestamp:newDate().toISOString()};
console.error('[未捕获的 Promise 错误]', errorInfo);reportError(errorInfo);// 这里建议不要 preventDefault,让错误继续暴露,开发时能及时发现});// 还有个对应的 handledrejection,用来监听那些被补 catch 的错误// 这个一般用得少,主要是做统计或者清理工作
window.addEventListener('rejectionhandled',function(event){
console.log('刚刚那个错误被 catch 住了:', event.reason);});
这里有个血泪教训:很多开发者喜欢在 async 函数里一把梭,所有 await 都不包 try-catch,指望全局监听兜底。这想法很危险,因为
unhandledrejection
触发的时候,你的代码执行上下文可能已经丢了,想做错误恢复或者重试都没戏。所以全局监听只能是最后一道防线,不能当主力用。
try-catch 的手动挡模式
有些逻辑你得自己包起来,特别是那些第三方库调用或者复杂的业务计算。别指望浏览器全包圆,它没那个义务。
// 基础用法,地球人都会try{const result =JSON.parse(userInput);processData(result);}catch(error){
console.error('JSON 解析失败:', error);showToast('数据格式不对,请检查输入');}// 但是 async/await 里的 try-catch 有点讲究asyncfunctionfetchUserData(userId){try{// 这里如果 fetchUser 抛错,会被 catch 住const user =awaitfetchUser(userId);// 如果 fetchOrders 抛错,也会被 catch 住const orders =awaitfetchOrders(user.id);return{ user, orders };}catch(error){// 问题来了:这里怎么知道是哪个接口挂了?// 简单业务还好,复杂业务得做错误分类if(error.name ==='NetworkError'){showToast('网络开小差了,戳这里重试');// 记录网络错误,可能是用户信号不好logError({type:'network', error });}elseif(error.response?.status ===404){showToast('用户不存在');// 业务错误,可能需要跳转 404 页面
router.push('/404');}else{// 未知错误,抛给上层或者全局处理throw error;}}}// 更优雅的错误处理封装// 定义一个 Result 类型,类似 Rust 或者 Go 的错误处理风格classResult{constructor(ok, data, error){this.ok = ok;this.data = data;this.error = error;}// 成功时返回数据,失败时返回默认值unwrapOr(defaultValue){returnthis.ok ?this.data : defaultValue;}// 链式处理map(fn){returnthis.ok ?newResult(true,fn(this.data),null):this;}}// 包装异步操作,让它永不抛错,总是返回 ResultasyncfunctionsafeAsync(promise){try{const data =await promise;returnnewResult(true, data,null);}catch(error){returnnewResult(false,null, error);}}// 使用示例,代码清爽多了asyncfunctionloadPageData(){const userResult =awaitsafeAsync(fetchUser());if(!userResult.ok){
console.error('获取用户失败:', userResult.error);return{error:'user_fetch_failed'};}const ordersResult =awaitsafeAsync(fetchOrders(userResult.data.id));// 即使 orders 挂了,user 数据还在,可以部分渲染return{user: userResult.data,orders: ordersResult.unwrapOr([])// 订单挂了显示空数组,别白屏};}Vue 和 React 的专属结界
现代框架都给了组件级的错误边界,这简直是救命稻草。组件挂了不至于全站崩盘,用户至少能看到个"出错了"的友好提示,而不是整页白屏。
Vue 的 errorHandler 配置:
// Vue 2 的全局配置
Vue.config.errorHandler=function(err, vm, info){// err: 错误对象// vm: 出错的组件实例// info: 错误信息,比如 "render function" 或者 "v-on handler"
console.error('Vue 捕获到错误:',{error: err.message,component: vm?.$options?.name ||'anonymous',info: info,// 可以拿到组件的 props 和 data,排查问题很有用props: vm?.$options?.propsData,data: vm?._data,// 组件调用栈trace: vm?.$options?.__file ||'unknown file'});// 上报错误reportError({type:'vue',message: err.message,stack: err.stack,component: vm?.$options?.name,info: info,timestamp: Date.now()});};// Vue 3 稍微变了一下,但思路一样import{ createApp }from'vue';const app =createApp(App);
app.config.errorHandler=(err, instance, info)=>{// 逻辑和上面差不多,instance 是组件实例
console.error('[Vue3 Error]', err, info);// 这里可以配合全局状态管理,显示个错误提示组件// 比如把错误信息塞到 Pinia 或者 Vuex 里useErrorStore().setError({message:'页面渲染出错了,刷新试试?',detail: err.message
});};// Vue 还提供了 warnHandler 专门抓警告,开发环境很有用
app.config.warnHandler=(msg, instance, trace)=>{// 警告一般不上报,但开发时可以打印详细点
console.warn('[Vue Warning]', msg, trace);};React 的 Error Boundary 稍微麻烦点,得用类组件:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// 错误边界必须是类组件,React 官方说的,函数组件暂时不行
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
// 静态方法,返回新的 state,用于渲染降级 UI
static getDerivedStateFromError(error) {
// 下次渲染时显示备用 UI
return { hasError: true, error };
}
// 组件DidCatch,用于记录错误信息
componentDidCatch(error, errorInfo) {
// errorInfo 包含 componentStack,能看到是哪个组件树出的问题
console.error('ErrorBoundary 捕获到错误:', {
error: error.toString(),
componentStack: errorInfo.componentStack,
// 可以记录当前的路由信息
pathname: window.location.pathname,
// 用户信息,如果有的话
userId: localStorage.getItem('userId')
});
this.setState({
errorInfo: errorInfo
});
// 上报错误
reportError({
type: 'react',
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString()
});
}
// 提供重置功能,让用户能尝试恢复
handleReset = () => {
this.setState({
hasError: false,
error: null,
errorInfo: null
});
// 可以调用父组件传入的重置回调
this.props.onReset?.();
};
render() {
if (this.state.hasError) {
// 自定义降级 UI
return (
<div className="error-fallback" style={{
padding: '40px',
textAlign: 'center',
background: '#fff2f0',
border: '1px solid #ffccc7',
borderRadius: '8px'
}}>
<h2 style={{ color: '#cf1322' }}>
版权声明:本文标题:Flash开发者秘籍:破解报错魔咒,提升效率,摆脱延时上线 内容由网友自发贡献,该文观点仅代表作者本人,
转载请联系作者并注明出处:https://www.betaflare.com/biancheng/1772317895a3273493.html,
本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
更多相关文章
Office2016 Pro Plus 64位免费版,迅雷链接即刻下载
转: 下载地址:ed2k:|file|SW_DVD5_Office_Professional_Plus_2016_64Bit_ChnSimp_MLF_X20-42426.ISO|1123452928|31087A00FF6
在您的电脑上安装Microsoft Office 2016中文镜像版
在使用上,零售版和批量授权版并没有区别,只是授权方式方面的区别,相对而言,VOL 版的更容易激活一些,其他并没有什么区别了。 有需要的可以在下面下载:(以下均是 VL 版)版本:Office 2016 Pro Pl
免费Mac Office 2016安装包,轻松上手教程
Mac Office 2016 安装包下载教程 本资源文件提供了适用于苹果系统的 Mac Office 2016 安装包下载教程,包括 Word、Excel 和 PowerPoint 的安装步骤。通过本教程,您可以轻松地在 M
Office2016回顾:历史版本与最新版的完美融合,一步步教你安装!
为什么重新修改这篇文章,因为最近又用到了Oracle水晶球需要office2007支持哈哈一台电脑可以安装两个不同版本的Office。在安装时,需要注意以下几点: 确保两个版本不会相互冲突。
不用麻烦,手动设置IE为你的默认浏览器体验
IE本身就是系统默认浏览器,但有时可能会一不小心将其他浏览器设置成了默认浏览器,要恢复IE为默认浏览器可以采取如下的方法。(1)对于Mozilla这类不采用IE内核的浏览器:可以打开IE,选择“工具→Internet选项→程序”,在“检查
Adobe Flash Player迷思解答:锁定IE文档模式10,告别所有兼容性困扰!
知识点 1.vue 只兼容ie8以上版本;2.IE 不兼容 axios的promise对象;3.IE 不兼容es6语法; 问题描述 工程使用的 vue2.X,而且
IE浏览器的意外双开秘密:解密快捷方式双网页现象
问题: ie设置为默认浏览器后,然后ie设置一个快捷方式到桌面,打开快捷方式, 竟然弹出一个是ie浏览器,一个是360浏览器,记得明明设置ie为默认了, 还能弹出2个浏览器。 原因: 虽然ie设
搞定Win下的默认浏览器:为何总是IE_Web当道?
今天开始打开项目时,突然间发现我的浏览器被改成了IE打开。奇怪了,并没有设置过默认浏览器为IE! 随后,当然是修改默认浏览器了,如下常规操作: 控制面板》程序》默认程序》设置默认程序》web浏览器》点击并选着你要设置的
步骤解析:把Internet Explorer变成你的默认浏览工具
IE本身就是系统默认浏览器,但有时可能会一不小心将其他浏览器设置成了默认浏览器,要恢复IE为默认浏览器可以采取如下的方法。(1)对于Mozilla这类不采用IE内核的浏览器:可以打开IE,选择“工具→Internet选项→程序”,在“检查
一步到位!Win7中快速设置IE为默认浏览器
在win7系统中,自带有ie浏览器,但是有些用户可能会安装其他浏览器,这样原先默认IE浏览器就会被篡改,习惯了使用IE浏览器的用户们就不喜欢了,那么要如何设置IE为默认浏览器呢,具体步骤如下。 1、首先从开始菜单或者打开“计算
一招搞定IE10设置!轻松锁定文档模式,告别Adobe Flash Player的兼容性困扰!
知识点 1.vue 只兼容ie8以上版本;2.IE 不兼容 axios的promise对象;3.IE 不兼容es6语法; 问题描述 工程使用的 vue2.X,而且
为何IE的Flash中心快捷方式总是开启两个浏览器窗口?
问题: ie设置为默认浏览器后,然后ie设置一个快捷方式到桌面,打开快捷方式, 竟然弹出一个是ie浏览器,一个是360浏览器,记得明明设置ie为默认了, 还能弹出2个浏览器。 原因: 虽然ie设
Windows 用户的心痛:默认浏览器设置为何一不小心就‘换回’IE?
今天开始打开项目时,突然间发现我的浏览器被改成了IE打开。奇怪了,并没有设置过默认浏览器为IE! 随后,当然是修改默认浏览器了,如下常规操作: 控制面板》程序》默认程序》设置默认程序》web浏览器》点击并选着你要设置的
摆脱电脑束缚,用手机轻松管理TP-LINK路由器
用手机设置TP-LINK路由器的时候,怎么才能进入TP-LINK路由器的登录入口? 为什么我操作老是失败,总是进不去设置页面,是我的路由器坏掉了吗? 答:手机设置tplink路由器的时候,如果手机无法进入到tp
一键解除网络限速,让电脑畅享高速网络
电脑解除网络限速,让网速飞起来 在日常使用电脑的过程中,你是否经常发现自己的电脑网速明显比别人慢?尤其是在下载文件、观看视频或者进行网络游戏时,这种网速差异尤为明显。如果你也遇到了类似的问题,那么很有可能是系统默认限制了20%
Excel高手必备:TL431可调电压基准源的求解秘技
TL431可调电压基准源的Excel求解与应用 1. TL431可调电压基准源简介 TL431可调电压基准源在行业中应用广泛,它具有简单的配置、低成本和广泛的调节能力,深受电子工程师喜爱。其基本原理图如下: grap
突破视觉强化学习训练的‘视觉’障碍,MJX带来高效方案
突破渲染瓶颈:MJX助力视觉强化学习的高效训练方案 在机器人控制与自动驾驶等领域,视觉强化学习(Visual Reinforcement Learning, VRL)需要通过大量图像数据训练智能体,但传统物理模拟器的渲染速度往
深度学习加速新纪元:张量引擎TBE与深度学习编译器,加速你的训练与推理
深度学习框架 人工智能操作系统 训练&前向推理 深度学习框架发展到今天,目前在架构上大体已经基本上成熟并且逐渐趋同。无论是国外的Tensorflow、PyTorch,亦或是国内最近开源的MegEngine、MindSpore
现代计算的新篇章:把FEMCFD求解器封装为PyTorch或JAX函数
将传统有限元(FEM)或计算流体力学(CFD)求解器封装为深度学习框架(PyTorchJAX)的可调用模块,是实现物理约束生成式模型、逆向设计优化和多物理场联合建模的核心技术。以下从 框架选择、
Excel宏数量爆炸,开机慢如乌龟?轻松破解攻略!
Excel 2003 今天下午,想琢磨以下Excel加载宏里的那些工具,把所有的宏都给选上了.这下可好,关掉Excel再打开,Excel就动静了,连续好几次都不行 开始还不知道是加载了过多宏的问题,还以为自


发表评论