作者简介:Jarvis,货拉拉资深前端工程师,负责货运司机履约相关的开发工作,专注提升前端系统的稳定性与排障效率
在前端开发中,页面白屏是一个关键指标。一方面,页面加载时白屏时间过长会显著影响用户体验;另一方面,异常场景下白屏更是系统稳定性的一大隐患。
本文将介绍前端项目白屏监测的一种轻量化实现,并探讨对应的治理思路。
通过技术手段监测并上报页面白屏现象,我们能够在用户反馈之前主动发现问题,从而提升用户体验、及时解决潜在隐患,并为系统稳定性提供了额外的保障维度。
渲染时发生 JS error,页面就会白屏,常见于 React 项目。
前端路由未匹配到,业务代码也未配置 404 兜底组件,常见于各类投放的 H5 url 被设置错误时。
未发生 JS error,但也没走到正确的渲染路径,业务代码中常见以下写法:
if (loading) {
return null;
}
return <div>...业务内容</div>;
// 或
// <div style="height: 100vh">
// <template v-if="!loading; ">
// ...业务内容
// </template>
// </div>
表现如下图:
在用户进入页面、包括初次进入页面和单页应用发生路由变化 debounce 3s 后,使用 document.elementsFromPoint
方法,如下图,在屏幕横、纵两条中线均匀取若干个点
判断屏幕两条中线取到的都是同一个元素,则认为发生了白屏
综合评估
document.elementsFromPoint
在移动端的兼容性相对较好,若有特殊兼容需要可使用兼容性更好的document.elementFromPoint
方法
// 判断是否同一个元素
const isSameElement = (nodeList: Element[]): boolean => {
// 不必实现
};
// 打点采样对比
const sampling = (reportWhiteScreen: : (res: {
status: 'ok' | 'error';
whiteElement?: {
id: string;
className: string;
tagName: string;
};
}) => any) => {
const sampledElements: Element[] = [];
const innerWidth = window.innerWidth;
const innerHeight = window.innerHeight;
for (let i = 1; i <= 19; i++) {
// 页面横/纵两条中线,十字取样
const xElements = document.elementsFromPoint((innerWidth * i) / 20, innerHeight / 2);
const yElements = document.elementsFromPoint(innerWidth / 2, (innerHeight * i) / 20);
sampledElements.push(xElements[0]);
// 中心点只计算一次
if (i !== 10) {
sampledElements.push(yElements[0]);
}
}
if (isSameElement(sampledElements)) {
reportWhiteScreen(sampledElements[0])
}
};
// pageview 发生时,debounce 3s 后检测
export const debounceCheckWhiteScreen = () => {
// 此处空函数指上报日志的通用方法
sampling(() => {});
};
当然,真实的场景肯定更复杂:我们的前端页面往往运行在不同的容器中,不同业务场景也需要兼顾到位。
因此团队在回收了一批日志后,针对多类场景都做了处理,简单列举几类:
首先回顾一下什么是可替换元素:
可替换元素 (replaced element) 的展现效果不是由 CSS 来控制的。这些元素是一种外部对象,它们外观的渲染,是独立于 CSS 的。
简单来说,它们的内容不受当前文档的样式的影响。CSS 可以影响可替换元素的位置,但不会影响到可替换元素自身的内容。某些可替换元素,例如
<iframe>
元素,可能具有自己的样式表,但它们不会继承父文档的样式。来源:MDN 可替换元素
在白屏检测的场景下,不必检测这类可替换元素是否有内容。
例如一个全屏的 iframe 是否发生了白屏,提供该 iframe 的团队自行检测即可;再或者一个全屏的 img (常见于运营投放的公告类信息),若图片资源加载失败、也已经有对应的资源加载成功率可以监控。
因此,我们白屏检测的部分可以进一步约定:只需要检测常见的用来做容器的节点的元素即可
const COMMON_CONTAINER_TAG_NAMES = [
'HTML',
'BODY',
'DIV',
'NAV',
'MAIN',
'SECTION',
'FOOTER',
'HEADER',
];
if (COMMON_CONTAINER_TAG_NAMES.indexOf(whiteElementTagName) > -1) {
reportWhiteScreen();
}
场景: 如下图,同时存在 2 个页面时,后方的 webview 若对于用户不可见,此时检测白屏也没有意义;这种场景常见于移动端,例如小程序或客户端同时打开了 2 个原生页面
解决方案: 检测时增加页面 visibleState
的判断,页面不可见时不检测白屏
场景: 某个接口发生正常业务报错,一般都是 toast 出对应的错误信息,此时执行检测白屏正常;3s 后 toast 消失,此时页面仍然是白屏
解决方案 :判定为正常的情况下,也兜底二次确认
当然,不是所有的白屏上报都属于异常场景:有可能是当时网络慢,检测的时候确实是白屏;也有的页面在业务定义上,就是一个没有内容的中转页面。
原因: 在执行检测白屏时,因网络等原因,资源或接口尚未加载完成,检测的那一时刻确实是白屏的。
回收上报的白屏数据发现,若页面的 FCP 75 分位大于 2s,上报的白屏比例 (白屏数量 / pv 数量) 会大于 0.8%。
解决方案: 需要业务代码优化,例如加个骨架屏
此类在业务上定义就是空页面的,在设置告警或排查白屏问题时剔除即可
处理完各类差异化场景,我们的白屏检测流程大致如下
当然,我们的方案预期内就会存在一定的误报,前文也提到了一些白屏但正常的场景。这并不影响,我们的目标是对于页面白屏指标能观测、并且可以针对性的排障。若有误报,只需要在排查时剔除对应的路由即可。
当前也有几类主流的白屏检测方案,在调研时团队也针对公司的业务场景做了分析和权衡,对比如下
有了白屏检测能力后,我们要如何发现并解决 H5 页面存在的白屏问题呢?
得益于货拉拉完善的日志系统和监控告警平台,我们可以针对上报的白屏日志配置看板和告警规则
设置项目页面路由级别的白屏看板,可在日常巡检时、或发生白屏告警后,观察页面的白屏上报情况以及发展趋势
针对与上一天同一个时间点的白屏比例 (当前项目的白屏数量 / 当前项目的 pageview 数量) 对比,在大于告警阈值的时候通知对应研发
下图是一次生产故障演练时的告警通知
除了对新增的白屏 case 进行观察和告警,线上存量的异常白屏场景往往是以下几类原因:
那么就需要结合上报的其他日志细致排查个例的操作轨迹,例如是否有接口数据异常、是否发生了 JS Error 等,一一排查。
本文探讨了一种轻量化的前端白屏监控方案。目前,该上报方案已经覆盖了货拉拉国内的所有 H5 页面,在告警系统上增加了通用的页面白屏率告警,高频页面也有初步定制化的告警规则。
通过走查上报的白屏日志,不仅发现了研发自身代码不当导致的白屏,也有业务侧的投放错误导致的问题,证实此方案确实可行。
接下来,我们的治理方案将会进一步精细化:
逐页面排查上报的案例,定位解决问题
持续监控相关指标,防止治理成果劣化
通过跟进上述措施,相信前端白屏监控能力能有效助力提升用户体验、进一步保障系统稳定性。