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

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

毒舌时刻

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

从vw/vh到clamp(),前端响应式设计的痛点与进化

从vw/vh到clamp(),前端响应式设计的痛点与进化

目录 从vw/vh到clamp(),前端响应式设计的痛点与进化 一、原生响应式设计的痛点 1、使用 vw/vh/% 的蜜月期与矛盾点 2、以 px+@media 为主轴实现多端样式兼容 二、clamp():响应式设计的新思路 1、clamp() 是什么? 2、优势分析 三、实际应用场景示例 1、标题文字大小 2、布局容器宽度 3、按钮与间距 4、配合calc()实现更灵活布局 四、clamp() 的局限与思考 五、结语 从vw/vh到clamp(),前端响应式设计的痛点与进化 一、原生响应式设计的痛点 1、使用 vw/vh/% 的蜜月期与矛盾点

AI 直接生成前端代码:我的软件原型设计流,从此告别重复画图

AI 直接生成前端代码:我的软件原型设计流,从此告别重复画图

近年来,AI 辅助开发越来越成熟,尤其是在快速原型设计方面。今天分享一下我如何借助 Cursor、Trace solo、ChatGPT、Qoder 等 AI 工具,高效完成软件原型的自动绘制与代码生成。 📌 核心流程三步走 1️⃣ 用 AI 输出需求文档(非技术描述) 首先,我会让 AI 根据产品思路或功能描述,生成一份清晰、无技术细节的需求文档。这一步不写代码,只聚焦逻辑与用户流程。 2️⃣ AI 生成 HTML 原型代码 基于上一步的需求文档,直接让 AI 生成对应的 HTML 代码,快速搭建出可交互的前端原型。支持实时预览,直观看到界面效果。 3️⃣ 反复微调,直至满意 生成的原型往往需要多次调整。通过自然语言描述修改方向,AI 可快速迭代代码,直至达到想要的交互与视觉效果。

前端岗面试30万字原题含答案

如果你在找**“前端岗面试30万字原题含答案”这种整套资料,一般是一些整理好的 前端面试题库合集(HTML / CSS / JS / 框架 / 工程化 / 算法)。我可以给你一份高质量完整版结构 + 常见原题示例答案**,也可以帮你整理成 PDF / Markdown / 学习路线。先给你一个真实企业常问题库结构👇 前端面试题大全(高频原题+答案整理版) 一、HTML 面试题 1. HTML5 新特性有哪些? 答: HTML5 新增: * 语义化标签 * <header> * <nav> * <section> * <article> * <footer> * 多媒体标签 * <audio>

Chromedriver下载地址找不到?GLM-4.6V-Flash-WEB识别官网布局

GLM-4.6V-Flash-WEB:用视觉大模型破解网页自动化中的“定位困局” 在现代软件开发与测试的日常中,一个看似简单却频繁出现的问题正不断消耗着工程师的时间——Chromedriver 下载地址变了,脚本又断了。 这并不是代码写得不好,也不是网络出了问题,而是目标网站(比如 https://sites.google.com/chromium.org/driver/)悄悄改版了。原本通过 XPath 或 CSS 选择器精准定位的“下载链接”,一夜之间消失不见,CI/CD 流水线随之中断。更麻烦的是,这类页面往往没有稳定的 API 接口,只能依赖前端渲染结果来获取信息。 传统解决方案无非两种:要么人工定期检查并更新路径规则,要么用 OCR 提取文字后配合正则匹配。但这些方法本质上都是“静态适配”——一旦页面结构调整、按钮换位置、语言切换成中文或法文,整套逻辑就可能失效。 有没有一种方式,能让自动化系统像人一样“看懂”