admin 管理员组文章数量: 1184232
前端老鸟血泪史:搞不定报错?这套错误处理策略让你少加三天班!
开篇先唠两句
别划走,我知道你刚被控制台那一堆红字搞崩了心态。那种看着满屏飘红的绝望,我懂——上周三凌晨两点,我盯着生产环境那个
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' }}>
版权声明:本文标题:避免加班的神技:前端开发中SWF故障诊断与解决策略分享 内容由网友自发贡献,该文观点仅代表作者本人,
转载请联系作者并注明出处:http://www.roclinux.cn/b/1772317543a3554562.html,
本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
更多相关文章
192.168.0.1设备探索:零基础入门
有不少的用户在反馈,说在的时候,登录入口打不开找不到,从而无法对进行设置,问我应该怎么办? 根据鸿哥的经验来看,出现无法打开的登录入口问题,绝大数情况下是用户自己操作有误引起的,极少数情况
告别重装系统,用DISM轻松解决电脑问题
介绍了解: DISM(部署映像服务和管理)是三种 Windows 诊断工具中最强大的。当遇到频繁的崩溃、冻结和错误,或者 SFC 要么无法修复您的系统文件,或者根本无法运行时,可以使用该工具。 相连文章: 修复
Dism工具大揭秘:Windows 10操作系统的幕后英雄
目录一、DISM是什么 DISM,英文名称为Deployment Imaging and Management,部署映像服务和管理。这是一个命令行工具,用于部署前从.wim文件、.ffu文件、.vhd文件或.vh
Dism助力:快速上手实现Flash Player无缝安装与更新
相关文章推荐:Windows ADK 下载地址: 命令示例:Gimagex图形化演示:以下命令由DISMGUI生成,原汁原味1.首次备份镜像【Captu
一文解密Dism++:卸载驱动的超高效方法
资源说明 Dism++(系统精简利器)是一款功能全面的Windows系统精简工具,在某种程度上可以说是以前的Dism管理器的升级版(最开始的名字叫Windows更新清理工具),Dism++(系统精简利器)全新的构建,更小的体积
DISM++:你的Flash播放问题终结者,提升性能
简介:DISM++是一款全方位的电脑维护软件,提供深度扫描和清理功能,专为优化个人计算机而设计。它能够高效清除各种系统垃圾和无用文件,释放硬盘空间,并通过系统清理、优化、备份和恢复功能提高电脑的运行速度和性能。该软件还支持多语言界面,
告别系统崩溃,通过DISM工具让电脑重获新生
介绍了解: DISM(部署映像服务和管理)是三种 Windows 诊断工具中最强大的。当遇到频繁的崩溃、冻结和错误,或者 SFC 要么无法修复您的系统文件,或者根本无法运行时,可以使用该工具。 相连文章: 修复
深度解析Dism++:打造Windows的私人优化专家
Dism++终极指南:免费高效的Windows系统优化解决方案 Dism++是一款功能强大的Windows系统优化工具,通过Dism-Multi-language项目提供全面的多语言支持,让全球用户都能以母语轻松使用其强大的系
掌握Windows 10的Dism技巧,让系统管理更高效、更便捷
目录一、DISM是什么 DISM,英文名称为Deployment Imaging and Management,部署映像服务和管理。这是一个命令行工具,用于部署前从.wim文件、.ffu文件、.vhd文件或.vh
一文读懂Dism命令行,Adobe Flash Player安装不再难!
相关文章推荐:Windows ADK 下载地址: 命令示例:Gimagex图形化演示:以下命令由DISMGUI生成,原汁原味1.首次备份镜像【Captu
Dism命令新探索:深入理解与实践Windows映像文件维护
Dism是什么? dism 命令(Deployment Image Servicing and Management)是Windows操作系统中的一个命令行工具,用于管理和维护映像文件(如Windows安装映像或修复映像)。d
Dism日志入门:轻松掌握Windows系统维护技巧!
使用DISM命令修复系统注意:DISM命令只会修复系统自带的文件,第三方软件、驱动问题使用此命令修复是无效的,修复过程是比较漫长的,但是修复期间不会影响你系统正常使用、也不会卡什么的,占用资源比较低。 一、检查映像
Windows Server系统备份与恢复:实战教程
1、添加windows server backup功能 a)选择添加角色和功能 b)选择功能中勾选“windows server backup”,然后“下一步” c)安装功能 2、使用windows s
C盘大搬家?别怕,Ghost备份带你安全过!
推荐用U盘制作启动工具 大白菜or老毛桃 备份:以老毛桃为例,进入PE,点击左下角开始,可见ghost功能选项 运行Ghost后,单击“OK”。 选择“Local”→“Partition”→“
Adobe Flash Player的未来发展趋势预测
目录背景: 在日常的工作中,由于我的笔记本自带的SSD固态硬盘是512G的容量,平时下几个大型的文件或者资料就要快满了,于是决定换一个1TB的固态硬盘,换之前首先确认自己现在用的是什么类型的固态硬盘,推荐大家一款
Linux备份与恢复必修课:SWF文件安全策略从入门到精通
在linux工作,系统备份是很有必要的,养成系统备份的好习惯会提高你的工作效率。下面我就简单的说一下:1.备份系统首先成为root用户:sudo su然后进入文件系统的根目录
GHOST教程:系统备份和还原,小白也能变成高手!
一、备份的概述1、定义:对关键系统、硬件等数据进行复制,当发生灾难时能快速的恢复原有数据,保证系统的正常稳定2、备份的方式外部冗余设备 (移动硬盘或U盘复制了电脑上重要数据)硬
Ubuntu系统安全大计,备份技巧大公开
本文主要参考这个博客。全文一半内容是复制粘贴的这个博客内容,提前声明一下,以防侵权。还参考了下这个ubuntu有时候用着用着崩了,或者想回退到历史某个版本。这就需要系统备份了:把当前某个能用的状态备
Win10系统备份轻松搞定:掌握captureimage命令的关键技巧
Win10自带的备份工具备份系统Windows操作系统经过从win98,win2000,winxp,win7,win8到win10的不断更新和完善,功能已经非常强大、完备了。但伴随着微软把重点转移到云端,对更新维护不再保
SWF文件备份失败?这些步骤让你轻松搞定
数据备份与恢复、系统备份与恢复 一、数据备份与恢复 1、什么是备份 备份,即另外准备一 – 为应付文件、数据丢失或损坏等可能出现的意外情况,将电子计算机存储设备中的数据复制到大容量存储设备中 2
发表评论