前端错误处理最佳实践:别让你的应用崩溃了!

前端错误处理最佳实践:别让你的应用崩溃了!

毒舌时刻

错误处理?听起来就像是前端工程师为了显得自己很专业而特意搞的一套复杂流程。你以为随便加个try-catch就能解决所有错误?别做梦了!到时候你会发现,错误处理的代码比业务代码还多,维护起来比业务代码还麻烦。

你以为console.error就能记录所有错误?别天真了!console.error只会在控制台打印错误,用户根本看不到,也无法帮助你分析错误原因。还有那些所谓的错误监控工具,看起来高大上,用起来却各种问题。

为什么你需要这个

  1. 提高用户体验:良好的错误处理可以避免应用崩溃,提高用户体验。
  2. 减少生产环境问题:及时捕获和处理错误可以减少生产环境中的问题。
  3. 便于调试:良好的错误处理可以帮助你更快地定位和解决问题。
  4. 提高代码可靠性:错误处理可以提高代码的可靠性,减少意外情况的发生。
  5. 监控和分析:错误处理可以帮助你监控和分析应用的运行状态,发现潜在问题。

反面教材

// 1. 忽略错误 function fetchData() { fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)); } // 2. 过度使用try-catch function processData(data) { try { if (data) { try { const processedData = data.map(item => { try { return item.value * 2; } catch (error) { console.error('Error processing item:', error); return 0; } }); return processedData; } catch (error) { console.error('Error mapping data:', error); return []; } } else { return []; } } catch (error) { console.error('Error processing data:', error); return []; } } // 3. 错误信息不明确 function calculateTotal(price, quantity) { if (typeof price !== 'number') { throw new Error('Invalid input'); } return price * quantity; } // 4. 缺少全局错误处理 window.addEventListener('error', (event) => { console.error('Global error:', event.error); }); // 5. 不记录错误 function login(username, password) { if (!username || !password) { throw new Error('Username and password are required'); } // 登录逻辑 } 

问题

  • 忽略错误,导致应用崩溃
  • 过度使用try-catch,导致代码变得臃肿
  • 错误信息不明确,难以定位问题
  • 缺少全局错误处理,无法捕获所有错误
  • 不记录错误,无法分析错误原因

正确的做法

错误处理策略

// 1. 基本错误处理 async function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); throw error; } } // 2. 自定义错误类 class ApiError extends Error { constructor(message, status) { super(message); this.name = 'ApiError'; this.status = status; } } async function fetchUserData(id) { try { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new ApiError(`Failed to fetch user: ${response.status}`, response.status); } const data = await response.json(); return data; } catch (error) { if (error instanceof ApiError) { console.error('API error:', error.message); } else { console.error('Unexpected error:', error); } throw error; } } // 3. 错误边界(React) class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { console.error('Error caught by boundary:', error, errorInfo); } render() { if (this.state.hasError) { return <div>Something went wrong. Please try again later.</div>; } return this.props.children; } } // 4. 全局错误处理 window.addEventListener('error', (event) => { console.error('Global error:', event.error); // 发送错误到监控服务 sendErrorToMonitoring(event.error); }); window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise rejection:', event.reason); // 发送错误到监控服务 sendErrorToMonitoring(event.reason); }); 

错误监控

// 1. 使用Sentry // 安装 // npm install @sentry/react // 初始化 import * as Sentry from '@sentry/react'; import { BrowserTracing } from '@sentry/tracing'; Sentry.init({ dsn: 'YOUR_DSN', integrations: [new BrowserTracing()], tracesSampleRate: 1.0, }); // 捕获错误 try { // 可能会出错的代码 } catch (error) { Sentry.captureException(error); } // 2. 自定义错误监控 function sendErrorToMonitoring(error) { // 收集错误信息 const errorData = { message: error.message, stack: error.stack, url: window.location.href, userAgent: navigator.userAgent, timestamp: new Date().toISOString() }; // 发送到监控服务 fetch('/api/error-monitoring', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorData) }); } // 3. 错误日志 function logError(error, context = {}) { console.error('Error:', error); console.error('Context:', context); // 发送到服务器 sendErrorToMonitoring({ ...error, context }); } 

用户友好的错误提示

// 1. 错误提示组件 function ErrorMessage({ error, onRetry }) { return ( <div className="error-message"> <h3>Oops! Something went wrong.</h3> <p>{error.message}</p> {onRetry && <button onClick={onRetry}>Try Again</button>} </div> ); } // 2. 表单错误处理 function LoginForm() { const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const handleSubmit = async (e) => { e.preventDefault(); setError(null); setLoading(true); try { // 登录逻辑 } catch (error) { setError(error); } finally { setLoading(false); } }; return ( <form onSubmit={handleSubmit}> {error && <ErrorMessage error={error} />} {/* 表单字段 */} <button type="submit" disabled={loading}> {loading ? 'Loading...' : 'Login'} </button> </form> ); } // 3. 异步操作错误处理 function DataFetcher() { const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error('Failed to fetch data'); } const data = await response.json(); setData(data); } catch (error) { setError(error); } finally { setLoading(false); } } fetchData(); }, []); if (loading) return <div>Loading...</div>; if (error) return <ErrorMessage error={error} onRetry={() => window.location.reload()} />; return <div>{/* 渲染数据 */}</div>; } 

最佳实践

// 1. 分层错误处理 // 底层:捕获并转换错误 async function apiCall(url, options) { try { const response = await fetch(url, options); if (!response.ok) { throw new ApiError(`API error: ${response.status}`, response.status); } return await response.json(); } catch (error) { if (error instanceof ApiError) { throw error; } else { throw new ApiError('Network error', 0); } } } // 中层:处理业务逻辑错误 async function getUserData(id) { try { return await apiCall(`/api/users/${id}`); } catch (error) { if (error.status === 404) { throw new Error('User not found'); } else { throw error; } } } // 上层:处理UI错误 function UserProfile({ userId }) { const [user, setUser] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function loadUser() { try { const userData = await getUserData(userId); setUser(userData); } catch (error) { setError(error); } finally { setLoading(false); } } loadUser(); }, [userId]); if (loading) return <div>Loading...</div>; if (error) return <ErrorMessage error={error} />; return <div>{/* 渲染用户信息 */}</div>; } // 2. 错误恢复策略 function AutoRetryComponent({ fetchData, maxRetries = 3 }) { const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [retryCount, setRetryCount] = useState(0); useEffect(() => { async function loadData() { try { const result = await fetchData(); setData(result); } catch (error) { if (retryCount < maxRetries) { setRetryCount(prev => prev + 1); // 延迟重试 setTimeout(loadData, 1000 * (retryCount + 1)); } else { setError(error); } } finally { setLoading(false); } } loadData(); }, [fetchData, maxRetries, retryCount]); if (loading) return <div>Loading...</div>; if (error) return <ErrorMessage error={error} />; return <div>{/* 渲染数据 */}</div>; } 

毒舌点评

错误处理确实很重要,但我见过太多开发者滥用这个特性,导致代码变得过于复杂。

想象一下,当你为了处理所有可能的错误,写了大量的try-catch块,结果导致代码量增加了几倍,这真的值得吗?

还有那些过度使用错误监控的开发者,为了捕获所有错误,在每个函数中都添加错误处理,结果导致代码变得难以理解和维护。

所以,在进行错误处理时,一定要把握好度。不要为了处理所有可能的错误而牺牲代码的简洁性,要根据实际情况来决定错误处理的策略。

当然,对于大型应用来说,良好的错误处理是必要的。但对于小型应用,过度的错误处理反而会增加开发成本和维护难度。

最后,记住一句话:错误处理的目的是为了提高应用的可靠性和用户体验,而不是为了炫技。如果你的错误处理策略导致应用变得更难维护或用户体验变得更差,那你就失败了。

Read more

目标检测数据集 第133期-基于yolo标注格式的无人机航拍人员搜救检测数据集(含免费分享)

目标检测数据集 第133期-基于yolo标注格式的无人机航拍人员搜救检测数据集(含免费分享)

目录 目标检测数据集 第133期-基于yolo标注格式的无人机航拍人员搜救检测数据集(含免费分享) 超实用无人机航拍人员搜救检测数据集分享,助力计算机视觉研究! 1、背景 2、数据详情 2.1 数据集规模与结构 2.2 标注类别 2.3 场景多样性 3、应用场景 3.1 应急搜救辅助系统 3.2 无人机智能监控与巡查 3.3 计算机视觉算法研究 3.4 灾害评估与灾后重建 4、使用申明 目标检测数据集 第133期-基于yolo标注格式的无人机航拍人员搜救检测数据集(含免费分享) 超实用无人机航拍人员搜救检测数据集分享,助力计算机视觉研究! 1、背景 在自然灾害、事故灾难等突发场景中,人员搜救是应急响应的核心任务。传统搜救方式受地形、天气、视野等条件限制,效率与安全性面临挑战。无人机航拍技术凭借其机动性强、

MacOS 安装 OpenClaw 并接入飞书机器人(保姆级教程 + 常见问题解决)

MacOS 安装 OpenClaw 并接入飞书机器人(保姆级教程 + 常见问题解决)

MacOS 安装 OpenClaw 并接入飞书机器人(保姆级教程 + 常见问题解决) 在 AI Agent 和自动化工具越来越普及的今天,越来越多开发者希望拥有一个 能够自动处理任务、接入团队协作工具的 AI 助手。 最近OpenClaw火的一塌糊涂,我也跟风研究了一下这个开源项目。它可以理解为一个 可扩展的 AI Agent 框架,支持接入各种工具、自动执行任务,并且可以和企业协作平台(如飞书)打通,实现 AI 自动回复、自动化工作流。 本文将带大家 从 0 开始,在 MacOS 上安装 OpenClaw,并接入飞书机器人。 同时我也整理了自己在安装过程中遇到的 终端报错问题与完整解决方案,让你一次性避坑。 本文包含: * MacOS 安装 OpenClaw * 接入飞书机器人 * 配置开机自启 * 终端报错解决(

KRS(Kratos Robotics Stack):让 Zynq / FPGA 机器人开发真正“跑”起来

在机器人与自主系统开发中,ROS 2 已经成为事实标准,但当它遇到 FPGA / Zynq / Kria 这类异构平台时,工程复杂度往往直线上升。 KRS(Kratos Robotics Stack) 正是为了解决这个问题而诞生的。 KRS 是一个由 Xilinx(AMD)官方推动的 开源机器人软件栈,目标非常明确: 让基于 ROS 2 的机器人应用,能够高效、可重复、工程化地运行在 Zynq 和 Kria 平台上。 先看下下面的应用视频: 🧠 什么是 KRS? KRS(Kratos Robotics Stack) 是一个围绕 ROS 2 + FPGA 加速 构建的完整开发框架,主要面向: * Zynq UltraScale+ MPSoC

【数据库】国产数据库的新机遇:电科金仓以融合技术同步全球竞争

【数据库】国产数据库的新机遇:电科金仓以融合技术同步全球竞争

7月15日,国产数据库厂商中电科金仓(北京)科技股份有限公司(以下简称“电科金仓”)在北京举行了一场技术发布会,集中发布四款核心产品:AI时代的融合数据库KES V9 2025、企业级统一管控平台KEMCC、数据库一体机(云数据库AI版)以及企业级智能海量数据集成平台KFS Ultra,并同步举行了“金兰组织2.0”启动仪式。 如果放在过去几年,这场发布会可能被归入“信创替代”的常规范畴。但这一次,电科金仓试图讲述的不再是“我们也能做、我们可以兼容”,而是“我们能不能定义下一代数据库形态”。 整个发布会贯穿了三个关键词:“融合”“AI”“平台能力”。这背后的核心逻辑是清晰的:在“去IOE”与“兼容Oracle”的红利渐近尾声之际,国产数据库厂商开始面对一个更加复杂、也更具挑战性的市场命题——如何在大模型时代支撑非结构化数据、高维向量检索和复杂语义计算的新需求? 正如我国数据库学科带头人王珊教授所说,数据库内核与AI能力的深度结合,已成为释放数据核心价值的关键路径,正催生着更智能、更自适应、更能应对复杂挑战的新一代数据库形态。