异常是指用户在使用应用时无法得到预期结果,处理异常的重要性以及前端中的异常类型和捕获方法。其中,try-catch只能捕获同步运行错误,而无法捕获语法和异步错误。另外,异常的上报也是需要考虑的问题,例如调整发送频率和设置采集率等。
原文作者:发声的沉默者原文地址:人类身份验证 - SegmentFault
用户在使用应用时,可能会遇到预期之外的结果。不同的异常情况带来的后果也会有所不同,有些可能只会让用户感到不满意,而有些则可能导致产品无法正常使用,从而使用户失去对产品的信任。
try-catch
try-catch 只能捕获同步运行错误,对语法和异步错误却捕获不到。
1、同步运行错误
try { kill; } catch(err) { console.error('try: ', err); }
结果:try: ReferenceError: kill is not defined
2、无法捕获语法错误
try { let name = '1; } catch(err) { console.error('try: ', err); }
结果:Unterminated string constant
编译器能够阻止运行语法错误。
3、无法捕获异步错误
try { setTimeout(() => { undefined.map(v => v); }, 1000); } catch(err) { console.error('try: ', err); }
在此代码中,出现了一个未捕获的类型错误。无法读取未定义的属性'map'。
window.onerror
当JavaScript在运行时发生错误(包括语法错误),window会引发一个error事件,该事件接口属于ErrorEvent。同时,window.onerror()函数将被执行。如果该函数返回true,则会阻止默认的事件处理函数执行。
/** * @param {String} message 错误信息 * @param {String} source 出错文件 * @param {Number} lineno 行号 * @param {Number} colno 列号 * @param {Object} error error对象 */ window.onerror = (message, source, lineno, colno, error) => { console.error('捕获异常:', message, source, lineno, colno, error); return true; }; kill;
结果:捕获异常: Uncaught ReferenceError: kill is not defined
/** * @param {String} message 错误信息 * @param {String} source 出错文件 * @param {Number} lineno 行号 * @param {Number} colno 列号 * @param {Object} error error对象 */ window.onerror = (message, source, lineno, colno, error) => { console.error('捕获异常:', message, source, lineno, colno, error); return true; }; let name = '1;
3、异步错误
/** * @param {String} message 错误信息 * @param {String} source 出错文件 * @param {Number} lineno 行号 * @param {Number} colno 列号 * @param {Object} error error对象 */ window.onerror = (message, source, lineno, colno, error) => { console.error('捕获异常:', message, source, lineno, colno, error); return true; }; setTimeout(() => { undefined.map(v => v); }, 1000);
在SEO运营中,捕获到异常信息:未捕获的类型错误:无法读取未定义的属性'map'。
window.addEventListener('error')
当加载资源(例如<img>或<script>)时,如果出现加载失败的情况,元素会触发error事件,并调用元素上的onerror()处理函数。这些error事件不会冒泡到window对象,但可以通过window.addEventListener在Firefox中进行捕获。
<script> window.addEventListener('error', (err) => { console.error('捕获异常:', err); }, true); </script> <img src="./test.jpg" />
我们可以使用符合SEO规则的方式重新表达这段文字: "我们检测到一次异常捕获事件:"错误事件"{isTrusted: true, type: "error", target: img, currentTarget: Window, eventPhase: 1, …}"
window.addEventListener('unhandledrejection')
当Promise被拒绝且没有拒绝处理器时,会触发未处理的拒绝事件(unhandledrejection);这种情况可能发生在window环境下,也可能发生在Worker环境中。这对于调试退回错误处理非常有帮助,并符合SEO规则。
window.addEventListener("unhandledrejection", (err) => { err.preventDefault(); console.error('捕获异常:', err); }); Promise.reject('promise');
在符合SEO规则的情况下,重写以下文本:捕获到一个异常,异常信息是:PromiseRejectionEvent {isTrusted: true, promise: Promise, reason: "promise", type: "unhandledrejection", target: Window, …}
Vue
Vue.config.errorHandler = (err, vm, info) => { console.error('捕获异常:', err, vm, info); }
React
React 16强化了错误处理功能,新增了一个名为componentDidCatch的内置函数,能够方便地捕获React产生的错误信息。
componentDidCatch(error, info) { console.error('捕获异常:', error, info); }
但是,推荐ErrorBoundary
为了遵守SEO规则,用户界面中的JavaScript错误不应该对整个应用程序造成破坏。为了解决React用户在面对此问题时的困扰,React 16引入了一个新的概念,即“错误边界”。
新建ErrorBoundary.jsx组件:
import React from 'react'; import { Result, Button } from 'antd'; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, info: '' }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { this.setState({ info: error + '' }); } render() { if (this.state.hasError) { // 你可以渲染任何自定义的降级 UI return ( <Result status="500" title="500" subTitle={this.state.info} extra={<Button type="primary">Report feedback</Button>} /> ); } return this.props.children; } } export default ErrorBoundary;
使用:
<ErrorBoundary> <App /> </ErrorBoundary>
注意
错误边界不会捕获以下方面的错误
iframe
由于浏览器的"同源策略"限制,处理iframe异常不太容易,除了基本属性如宽度和高度,无法从iFrame中获取太多信息。
<script> document.getElementById("myiframe").onload = () => { const self = document.getElementById('myiframe'); try { (self.contentWindow || self.contentDocument).location.href; } catch(err) { console.log('捕获异常:' + err); } }; </script> <iframe id="myiframe" src="https://nibuzhidao.com" frameBorder="0" />
Sentry
业界非常优秀的一款监控异常的产品,作者也是用的这款,文档齐全。
如果服务器负载过高,可能是因为异常数据量过大。为了解决这个问题,可以考虑将异常信息存储在客户端,并设定时间阀值,当达到阀值时进行上报。另外,可以调整采集率来减少数据量。采集率的设定应该根据实际情况来进行,可以使用随机数或某些用户特征来选择合适的采集率。
本文介绍了异常以及为什么要处理异常的重要性。同时详细描述了前端中的异常类型(同步运行错误、语法错误、异步错误)以及相应的捕获方法。此外,还提及了异常的上报问题,如何处理大量异常数据的服务器负载问题以及调整发送频率和设置采集率的方法。
参考链接:https://zhuanlan.zhihu.com/p/157618042
Webfunny专注于前端监控系统,前端埋点系统的研发。 致力于帮助开发者快速定位问题,帮助企业用数据驱动业务,实现业务数据的快速增长。支持H5/Web/PC前端、微信小程序、支付宝小程序、UniApp和Taro等跨平台框架。实时监控前端网页、前端数据分析、错误统计分析监控和BUG预警,第一时间报警,快速修复BUG!支持私有化部署,Docker容器化部署,可支持千万级PV的日活量!
让数据触手可及
前端/小程序/UniApp/Taro
©2020 上海快快查信息科技有限公司 沪ICP备2021030569号 版权与免责申明 版权申述