前端白屏检测:SDK的设计与实现
前言
前端白屏指页面在加载过程中长时间无法正常展示内容,内容区空白,使用户无法进行查看、保存等一切操作,这是非常严重的问题。如果能尽早检测到白屏问题,就可以及时处理,避免或降低负面影响。
白屏的检测手段有两种。一是真实用户端的检测,通过接入白屏检测SDK实现,无法在用户端白屏报错前发现问题,是被动监控的方式;另一种是自动化检测,在团队内部通过自动化工具模拟用户行为主动检测,可以提前发现问题。
本文为前端白屏检测的上篇,主要讲真实用户端的检测,即SDK的设计与实现。
白屏的表现与原因
白屏的通常表现为:
-
页面空白或仅显示背景色,没有实际内容
-
页面一直展示骨架屏,包括页面loading状态
-
页面只展示导航菜单,内容区空白,包括微前端或iframe嵌套子页面的场景
导致白屏的原因分两种:资源加载错误、代码执行错误。
检测方案对比
方案 | 原理 | 优点 | 缺点 |
---|---|---|---|
检测根节点是否渲染 | SPA框架渲染的 DOM 一般挂载在一个根节点下,监听onload、onerror事件,检测根节点下是否挂载 DOM | 开发成本低 | 通用性差,只兼容主流 SPA 框架 |
监听 DOM 变化 | 利用 Mutation Observer API 监听DOM变化 | 开发成本低 | 准确度低,无法检测未渲染、始终渲染骨架屏等情况,如果用户长时间未操作DOM可能会误判白屏 |
页面截图对比 | 对页面截图,将截图与纯白的图片做对比 | 技术栈无关,通用性好 | 准确度低,无法检测纯背景色、骨架屏的白屏场景 |
前端框架内置ErrorBoundary组件捕获异常 | 利用ErrorBoundary组件捕获JS执行异常检测白屏 | 开发成本低 | 无法检测资源异常导致的白屏,只兼容于特定框架应用,接入时对业务代码侵入大 |
页面关键点采样对比 | 在页面中垂直/交叉取多个采样点,用 elementsFromPoint API 获取采样点下的 HTML 元素,判断采样点元素是否与容器元素相同 | 准确度高,技术栈无关,通用性好 | 开发成本稍高 |
通过以上对比发现,采用「页面关键点采样对比」的实现方案较好。
需要注意的是,对于主应用内嵌入的iframe的场景,因为每次采样取到的都是整个iframe元素,所以无法在主应用侧判断iframe是否白屏,需要在iframe应用内接入白屏检测SDK。
流程图
数据采集
屏幕采样点选取
采样点的选取有三种方式:垂直采样、交叉采样、垂直交叉采样。
垂直采样
交叉采样
垂直交叉采样
很明显,采样点越多判断越准确,但计算量稍大一点,不过我们利用requestIdleCallback在浏览器空闲时计算。因此,我们选择垂直交叉的采样方式。
白屏的判断标准与检测时机
有骨架屏和无骨架屏应用的检测方式不一样,检测时机也有细微差别。
无骨架屏场景
检测时机
- document.readyState在complete时或load事件触发时
- 全局error事件触发时
- 全局unhandledrejection事件触发时
检测方式
初始化SDK时,我们需要配置哪些是根容器,如果根容器为空则说明是白屏。
具体实现方式为,根据屏幕的宽度(window.innerWidth)和高度(window.innerHeight)算出每个采样点的具体坐标,再用elementsFromPoint获取每个坐标的 dom 元素,对比获取的元素是否为配置的根容器元素。
仔细想一下,上面的判断方式其实会有问题。
因为在 微前端与iframe场景 下,子应用白屏时,应该也需要上报才对。如果按上述方式判断,主应用(一般包含导航或者一级菜单)如果没有白屏,子应用永远不会被检测出白屏。因此,需要兼容此类场景。
兼容方式也很简单,我们只要判断内容区内的采样点满足白屏条件即可。大部分后台类的应用,会有顶部导航或左侧的一级菜单,因此我们选定右下方为内容区。
如上图所示,整个屏幕共33个采样点,其中内容区有28个。简单起见,检测白屏时,我们判断空白的采样点是否大于等于28个。采样点坐标的获取如下:
1for (let i = 1; i <= 9; i++) {
2 // x轴采样点
3 const xElements = document?.elementsFromPoint((window.innerWidth * i) / 10, window.innerHeight / 2);
4 // y轴采样点
5 const yElements = document?.elementsFromPoint(window.innerWidth / 2, (window.innerHeight * i) / 10);
6 // 上升的对角线采样点
7 const upDiagonalElements = document?.elementsFromPoint(
8 (window.innerWidth * i) / 10,
9 (window.innerHeight * i) / 10,
10 );
11 // 下降的对角线采样点
12 const downDiagonalElements = document?.elementsFromPoint(
13 (window.innerWidth * i) / 10,
14 window.innerHeight - (window.innerHeight * i) / 10,
15 );
16
17 if (this.isContainer(xElements[0] as HTMLElement)) emptyPoints++;
18
19 // 中心点只计算一次
20 if (i !== 5) {
21 if (this.isContainer(yElements[0] as HTMLElement)) emptyPoints++;
22 if (this.isContainer(upDiagonalElements[0] as HTMLElement)) emptyPoints++;
23 if (this.isContainer(downDiagonalElements[0] as HTMLElement)) emptyPoints++;
24 }
25}
26
有骨架屏场景
检测时机
-
document.readyState在complete之前
-
全局error事件触发时
-
全局unhandledrejection事件触发时
检测方式
如果应用内有骨架屏,继续用无骨架屏应用的白屏检测方式已经无法判断白屏,因为骨架屏也是有效的 dom 元素。
有骨架屏应用的检测方式为:对比初次采样前后获取的 dom 元素是否一致。因为在页面加载完成前可能已经渲染完骨架屏,为了获取对照组数据,初次采样的时间要在页面加载完成前。
1// 项目有骨架屏
2if (this.isSkeletonApp) {
3 if (document.readyState !== 'complete') {
4 this.idleCallback({
5 type: 'beforeComplete',
6 message: '骨架屏场景白屏',
7 });
8 }
9} else {
10 // 页面加载完毕
11 if (document.readyState === 'complete') {
12 this.idleCallback({
13 type: 'complete',
14 message: '页面加载完毕白屏',
15 });
16 } else {
17 window.addEventListener(
18 'load',
19 this.idleCallback.bind(this, {
20 type: 'load',
21 message: '页面加载完毕白屏',
22 }),
23 );
24 }
25}
26
27window.addEventListener('error', (e) => {
28 this.idleCallback({
29 type: e.type,
30 message: e.message,
31 filename: e.filename,
32 lineno: e.lineno,
33 colno: e.colno,
34 });
35});
36
37window.addEventListener('unhandledrejection', (e) => {
38 this.idleCallback({
39 type: e.type,
40 reason: e.reason,
41 message: 'Promise未捕获的错误',
42 });
43});
44
数据上报
检测出白屏问题后,就要上报白屏信息到数据后台了。一般数据后台需要有数据清洗、存储、消费、告警等功能。此外,还需要区分不同的产品与环境,控制上报数据并发量、上报用户浏览器信息、用户行为数据、方便排查问题的Sourcemap,告警方式与规则等细节问题。如果要将数据后台做的全面细致,实现成本是比较高的。
权衡投入产出比后,我们的数据后台复用了云音乐部门同事研发的前端错误监控平台Corona。我们要做的就是将上报白屏错误到Corona的逻辑,内置到白屏检测SDK中。
SDK的接入方式
SDK支持以外链方式接入前端应用,除云音乐的Corona puzzle脚本外,不依赖其他资源加载,一般只需要改动模板文件,不侵入业务代码。
SDK API
配置项
字段名 | 类型 | 说明 | 是否必须 | 默认值 |
---|---|---|---|---|
containers | String[] | 需要检测白屏的容器选择器列表 | 否 | ['html', 'body', '#app', '#root', '#mainapp-container', '#subapp-container'] |
corona | Corona | Corona平台错误监控的实例 | 否 | window.corona |
isSkeletonApp | Boolean | 是否是有骨架屏的应用 | 否 | false |
autoInit | Boolean | 是否自动初始化SDK | 否 | true |
debug | Boolean | 是否开启调试模式,开启后会打印日志 | 否 | false |
方法
方法名 | 说明 | 参数 | 返回值 |
---|---|---|---|
init | 初始化SDK | 无 | 无 |
通过外链接入
只需要在模板文件内,通过外链script引入白屏检测SDK及其依赖的Corona SDK即可。
总结
本文首先介绍了前端白屏表现、白屏原因,以及修复白屏问题的业务价值。然后对比几种常见的白屏检测方案,并介绍了采样点检测方案的具体实现,包括采样点如何选取、白屏的判断标准与检测时机、微前端与iframe场景的兼容等。
白屏检测工具完善现有的质量保障体系。使我们能尽早发现、及时处理白屏问题,减少线上重大故障几率,降低白屏问题对客户的负面影响。下一篇文章将介绍如何自动化的检测白屏,模拟用户的行为,主动发现问题。