前端监控SDK的定义
前端监控SDK(Software Development Kit)是一种嵌入到前端应用程序中的工具包,用于实时监控页面性能、捕获异常错误、跟踪用户行为等关键指标,优化用户体验,提升应用稳定性。
一、性能监控
性能监控中需要监控的对象有
- 网页资源加载的内容(img,video,js...)
- 网页FCP、LCP、FP等
- 网络接口相关的数据
使用PerformanceObserver(性能监测对象)统计性能数据
PerformanceObserver 用于监测性能度量事件,在浏览器的性能时间轴记录新的 performance entry的时候将会被通知。
其中有个方法PerformanceObserver.observe():
指定监测的 entry types 的集合。当 performance entry 被记录并且是指定的 entryTypes 之一的时候,性能观察者对象的回调函数会被调用。
当监测到的时候可以调用PerformanceObserver.disconnect()停止接收 性能条目。
可以通过这个Api获取FP、FCP、LCP、CLS
性能指标
网页加载过程的五大阶段
- 1.网络响应阶段
- 2.首次渲染阶段
- 3.内容加载阶段
- 4.用户交互阶段
- 5.页面稳定阶段
web vitals指标按照时间顺序梳理
指标采集实际方式
额外指标:PV(页面浏览量)
实现:
-
performance.now()是浏览器提供的API,用于获取高精度的时间戳。它返回一个以毫秒为单位的浮点数,表示从性能测量开始到调用该方法时经过的毫秒数。
-
location.href和document.referrer是浏览器提供的API,分别用于获取当前页面的URL和引用来源URL。
-
getUUID()是一个自定义函数,用于生成一个唯一标识符(UUID)。具体实现方式可能是使用随机数、时间戳等来生成一个唯一字符串。
-
beforeunload事件在用户离开页面之前触发,可以用于执行一些清理操作或发送数据。在这里,我们使用它来计算停留时间并发送给追踪器。
-
追踪器是一个自定义对象或库,用于接收并处理跟踪数据。在这里,我们假设有一个名为tracker的对象,并调用其send()方法来发送数据。具体的实现可能会将数据发送到服务器或进行其他处理。
二、错误监控
2.1 代码错误
- 1、SyntaxError语法错误
const 11a; // Uncaught SyntaxError: Invalid or unexpected token
- 2、ReferenceError引用错误
console.log(a); // ReferenceError: a is not defined
- 3、RangeError范围错误
const arr = new Array(-10) // RangeError: Invalid array length
- 4、TypeError类型错误
const arr = new '123'; // TypeError: "123" is not a constructor
const obj = {}
obj.fn() // TypeError: obj.fn is not a function
- 5、跨域脚本错误
// 1.在跨域脚本资源的响应头中添加:
Access-Control-Allow-Origin: *
// 2.为 <script> 标签设置 crossorigin 属性:
<script src="https://example.com/script.js" crossorigin="anonymous"></script>
2.2 JS错误捕获
分为三种错误类型:资源加载错误、JS执行错误、跨域错误
2.3 资源加载错误捕获
2.4 promise错误捕获
promise的错误是通过监听unhandledrejection事件捕获。但捕获到的错误无法获取到错误文件还是错误行列数的。
当一个Promise被拒绝(rejected)时,如果没有通过catch()或者then()方法的第二个参数来处理这个拒绝,那么就会触发unhandledrejection事件。
unhandledrejection事件是一个全局事件,可以通过window对象的unhandledrejection属性来监听。当这个事件被触发时,可以获取到拒绝的原因(reason)和相关的Promise对象。
2.5 react错误捕获
React官方提供ErrorBoundary错误边界,被该组件包裹的子组件render函数报错时会触发离当前组件最近的父组件ErrorBoundary。可以通过componentDidCatch捕获错误
错误捕获的方法
- 监听error事件
- 全局错误监听机制,用于捕获页面中发生的 JavaScript 错误和静态资源加载错误。当页面中的 JavaScript 执行出现未捕获的异常,或者资源(如图片、脚本、样式表等)加载失败时,该事件会被触发,并提供详细的错误信息
window.addEventListener('error', (event: ErrorEvent | Event) => {
console.error('Error captured by event listener:', event);
// 上报错误信息到后台
}, true);
- window.onerror
- window.onerror 是一个全局的错误捕获事件,用于捕获页面中未被捕获的 JavaScript 错误。当页面中的 JavaScript 发生错误时,如果没有被 try-catch 捕获,这个错误会被 window.onerror 捕获并处理。
window.onerror = function (message, source, lineno, colno, error) {
console.error('Error captured:', { message, source, lineno, colno, error });
// 上报错误信息到后台
};
三、行为监控
行为分为两类:错误录屏,用户在页面的各种操作
错误录屏
当用户在页面进行操作时可能会发生一些错误,那么我们作为开发者,我们需要知道用户是进行了什么操作进而才导致出了错误,所以我们需要对页面进行一个录屏,还原出用户的错误操作,进而进行修复bug。
录屏方法调研 经过调研得出录屏的两种办法:WebRTC和rrweb[record and replay the web]
在实际场景中,webRTC需要征求用户的意愿才能开启,如果用户不同意那么就没办法录制,故选用rrweb
rrweb原理
-
生成快照:在页面开始时rrweb会先生成一份页面的快照,用于记录网页初始状态
- 快照的原理:使用深度优先遍历对 DOM 树进行序列化,记录每个节点的属性(如
tagName、id、className)、子节点、样式、位置信息等。 - 生成的快照数据结构是一个 JSON 格式对象,包含页面的完整初始状态。
- 快照的原理:使用深度优先遍历对 DOM 树进行序列化,记录每个节点的属性(如
-
捕获用户的操作:监听用户在页面上的所有操作事件,包括鼠标事件、键盘事件、滚动事件、窗口大小改变、视图变化等。这些事件被序列化成一系列“增量快照”。
事件序列化格式:
时间戳:事件发生的时间。
事件类型:例如
mousemove、click等。位置或内容变化:例如鼠标的位置、输入框的新值。
目标元素:事件发生的目标元素的引用。
-
增量快照
- 增量快照的作用:记录用户操作导致的页面变化,例如,用户点击一个按钮后,按钮的
className发生变化,这种局部变化会被记录为增量快照 - 增量快照的实现:使用
MutationObserver监听 DOM 的变化,记录新增、删除、修改的节点和属性
- 增量快照的作用:记录用户操作导致的页面变化,例如,用户点击一个按钮后,按钮的
-
事件轴管理
rrweb会为每个事件分配一个时间戳,用于记录事件的发生时间。时间戳有助于在回放时正确地还原事件的顺序和间隔 -
回放
- 根据初始快照重建 DOM 树,初始化回放页面。
- 依次读取事件流,根据时间戳和事件类型触发相应的操作
rrweb会根据事件流的时间戳计算事件之间的间隔,确保回放时的操作流畅且符合真实用户操作
用户行为收集:了解用户喜好,优化
- 鼠标行为
- 键盘行为
- 路由跳转行为
- 点击行为
路由
常见两种模式:history模式和hash模式。
- hash模式
hash模式是将URL中的#符号作为路由的切换标志,通过监听URL中#号后面的变化,从而实现页面的切换。在单页应用中广泛使用,常常配合window.location.hash属性和onhashchang、 onpopstate事件使用来监听和控制路由的变换。
export const proxyHash = (handler:FN1):void =>{
window.addEventListener('hashchange',(e)=>handler(e),true)
window.addEventListener('popstate',(e)=>handler(e),true)
}
- history模式
- window.history属性指向history对象,表示当前窗口的浏览历史。
四、异常监控
白屏监控
采用采样对比方法(通过动态采样页面关键点来判断页面是否处于白屏)
-
采样点矩阵策略:
- 在视窗区域内建立3x3网格(共9个采样点)
- 横向坐标:
(window.innerWidth * i)/10(i=1~9) - 纵向坐标:
(window.innerHeight * i)/10(i=1~9) - 中心点复用避免重复计算,共形成17个采样点
-
容器元素判定:
- 通过
containerElements配置白屏判定容器(如div#app) - 通过
skeletonElements配置骨架屏元素(如.ant-skeleton) - 使用
elementsFromPoint获取采样点最顶层元素
- 通过
页面卡顿---监控FPS来判断页面是否卡顿
页面崩溃---webWorker,通过心跳机制来实现页面崩溃的检测。
上报方式
了解下上报的几种方式:信标(Beacon API),Ajax(XMLHttpRequest 和 fetch),Image(GIF、PNG);它们是现代前端中用于网络请求的三种常见方式。它们各自有不同的使用场景和优缺点。
1.信标(Beaocn API)
Beacon API 是浏览器提供的一种网络请求机制,主要用于在页面卸载时进行异步传输统计或其他非关键性任务的通知。这种方式的主要优势在于可以始终保持异步,不会对浏览器的性能造成影响。同时,由于 Beacons 在页面卸载时能够保证发送成功,因此它非常适合用于网页统计等场景。但是该方式的缺点是,它只适用于发送少量且不需要返回值的数据,同时不支持跨域请求。
2. Ajax(XMLHttpRequest 和 fetch)
Ajax 技术主要通过 Ajax 来实现异步请求。它的主要优势在于可以方便地发送各种类型的数据和请求,同时也支持跨域请求。在实际开发中,Ajax 技术广泛地应用于网页的交互及数据请求。然而,该方式的缺点在于无法保证每个请求都能被及时响应,而且可能会阻塞其他请求,影响浏览器的性能。
3. Image(GIF、PNG)
使用 Image 来进行网络请求,在实际开发中往往用于上报日志、统计、广告等应用场景。它的主要优势在于能够确保所有的请求都能够被执行,而且请求发送过程不会阻塞其他操作。无需关心返回值,在一些特定场景下,Image 作为一种简单且有效的方法进行异步数据处理,很受欢迎。但是,该方式仅适用于发送少量的数据,而且能够通过 URL 的长度来限制数据量的大小。
最终采用 sendBeacon + xmlHttpRequest 降级上报的方式,当浏览器不支持 sendBeacon 或者 传输的数据量超过了 sendBeacon 的限制,我们就降级采用 xmlHttpRequest 进行上报数据;