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

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

毒舌时刻

错误处理?听起来就像是前端工程师为了显得自己很专业而特意搞的一套复杂流程。你以为随便加个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

基于web 火车票务管理系统设计与实现

基于web 火车票务管理系统设计与实现

博主介绍:翰文编程 专注于Java(springboot ssm 等开发框架) vue  .net  php phython node.js    uniapp 微信小程序 等诸多技术领域和课设项目实战、企业信息化系统建设,从业十八余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了2000+题目解决方法案例  方便大家学习使用 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人 文末下方有源码获取地址 3.4 系统总体设计 3.4.1 功能设计 火车票务管理系统主要用户信息管理与查看,管理员信息管理与查看,新闻信息管理与查看,列车信息管理与查看,途径站点信息管理与查看,订票信息管理与查看等功能,具体功能模块图如3.1所示: 图3.1 系统总体模块图 3.4.2 登录流程 当管理员需要登录的时候,

【技术干货】用 Claude 4.6 直接“写”出可上线的前端 UI:从画布工具到代码工作流的升级思路

【技术干货】用 Claude 4.6 直接“写”出可上线的前端 UI:从画布工具到代码工作流的升级思路

摘要 本文从 Google Stitch 热度切入,对比“AI 画布式 UI 生成”与“代码内 UI 生成”两种路径,系统拆解如何用 Claude 4.6 + 前端设计规则,在真实代码库中迭代出可上线的 UI。附完整 Python API 调用示例与提示词模板,并结合多模型平台薛定猫 AI 的接入方式,帮助前端/全栈开发者把 AI UI 生成直接融入开发流水线。 一、背景:从“好看截图”到“可上线 UI” 当前 AI UI 方向大致两类路径: 1. 画布式设计工具 代表:Google Stitch

0. 总纲|Java Web 自研框架 18 年Java架构决策复盘

0. 总纲|Java Web 自研框架 18 年Java架构决策复盘

深耕政务信息化 20 年,自研 Java Web 框架支撑省级新农保、全国跨省医保结算等核心民生系统,稳定运行 18 年。 本系列不讲空泛理论,只复盘真实生产环境下的架构决策、踩坑经历、落地方案,不求优雅,但求能跑、能扛、能维护。 在长期维护政务系统的过程中,我逐渐形成一套轻量、稳定、无侵入、可长期演进的架构思路。 这套框架没有依赖流行全家桶,而是围绕业务痛点一点点打磨,最终支撑了海量高并发、高可靠的民生业务。 本系列将从以下 10 个核心决策展开: 1. 放弃 Spring,手写轻量 IOC 容器 2. 注解路由 + 参数路由,实现新老代码平滑迁移 3. 统一入参解析,前后端彻底解耦 4. CGLIB + 责任链实现轻量 AOP,搞定事务、日志、

Spring 核心技术解析【纯干货版】- XV:Spring 网络模块 Spring-Web 模块精讲

Spring 核心技术解析【纯干货版】- XV:Spring 网络模块 Spring-Web 模块精讲

Spring Framework 作为 Java 生态中最流行的企业级开发框架,提供了丰富的模块化支持。其中,Spring Web 模块是支撑 Web 开发的基础组件,无论是传统的 MVC 应用,还是 REST API 及微服务架构,都离不开它的核心能力。 本篇文章将深入解析 Spring Web 模块的核心概念、依赖关系、作用及关键组件,并通过实际案例展示如何使用 Spring Web 进行 RESTful API 调用。本文力求内容精炼、干货满满,帮助你掌握 Spring Web 的核心技术点。 文章目录 * 1、Spring-Web 模块介绍 * 1.1、Spring-Web 模块概述 * 1.2、Spring-Web