前端H5白屏监控与实践

3,423 阅读9分钟

前言

在前端开发中,页面白屏是一个关键指标。一方面,页面加载时白屏时间过长会显著影响用户体验;另一方面,异常场景下白屏更是系统稳定性的一大隐患。

本文将介绍前端项目白屏监测的一种轻量化实现,并探讨对应的治理思路。通过技术手段监测并上报页面白屏现象,我们能够在用户反馈之前主动发现问题,从而提升用户体验、及时解决潜在隐患,并为系统稳定性提供了额外的保障维度。

常见白屏场景

场景一

渲染时发生 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 后又白了

场景: 某个接口发生正常业务报错,一般都是 toast 出对应的错误信息,此时执行检测白屏正常;3s 后 toast 消失,此时页面仍然是白屏

解决方案:判定为正常的情况下,也兜底二次确认

白屏但正常的场景

当然,不是所有的白屏上报都属于异常场景:有可能是当时网络慢,检测的时候确实是白屏;也有的页面在业务定义上,就是一个没有内容的中转页面。

页面加载慢

原因: 在执行检测白屏时,因网络等原因,资源或接口尚未加载完成,检测的那一时刻确实是白屏的。

回收上报的白屏数据发现,若页面的 FCP 75 分位大于 2s,上报的白屏比例 (白屏数量 / pv 数量) 会大于 0.8%。

解决方案: 需要业务代码优化,例如加个骨架屏

设计如此

此类在业务上定义就是空页面的,在设置告警或排查白屏问题时剔除即可

白屏检测流程

处理完各类差异化场景,我们的白屏检测流程大致如下

当然,我们的方案预期内就会存在一定的误报,前文也提到了一些白屏但正常的场景。这并不影响,我们的目标是对于页面白屏指标能观测、并且可以针对性的排障。若有误报,只需要在排查时剔除对应的路由即可。

方案对比取舍

当前也有几类主流的白屏检测方案,在调研时团队也针对公司的业务场景做了分析和权衡,对比如下

方案评估结论
页面截图对比颜色,检测是否纯色。优点 准确度高**不使用原因:**引入成本大:例如常见的 html2canvas 库,可以将页面转成一个图片,其体积就有58kb,对于「寸土寸金」的日志 SDK 而言太大,引入成本过高另外检测时由于要操作canvas、逐个对比像素,预期内就对用户设备的性能有较大损耗。
React 提供的ErrorBoundary,渲染出错时该生命周期会被执行不使用原因:不够通用:无法适用于其他技术栈下的前端项目;并且需要业务代码自行改造、上报,而我们的目标是一个通用性的监控方案;另外,也有上文提到的原因:白屏时不一定会发生 JS Error,二者没有直接的因果关系。
使用 document.elementsFromPoint 方法打点,轮询检测取点是否全部为根节点根节点(html,body,#app,#root 等)前端监控库 web-see 就是使用方案我们整体检测思路正是来自 web-see 的方案,但在原有方案上针对公司的业务场景做了扩展:通用性强:例如上文提到的场景三,web-see无法检测,我们的方案可以正常检测上报白屏。接入简单:在我们的方案下,无需业务代码参与改造,无需指定检测的根节点。轻量化:对原监控SDK的体积只增加了0.7KB(gzip后);真机测试检测平均耗时 8ms;影响小:检测时机在页面pv发生后,无须轮询浪费性能。适配场景多:如上检测流程图,我们针对 6 类不同场景 也做了兼容处理,保障了上报数据的准确程度。

排障思路

有了白屏检测能力后,我们要如何发现并解决 H5 页面存在的白屏问题呢?

得益于货拉拉完善的日志系统和监控告警平台,我们可以针对上报的白屏日志配置看板和告警规则

看板观察

设置项目页面路由级别的白屏看板,可在日常巡检时、或发生白屏告警后,观察页面的白屏上报情况以及发展趋势

告警通知

针对与上一天同一个时间点的白屏比例 (当前项目的白屏数量 / 当前项目的 pageview 数量) 对比,在大于告警阈值的时候通知对应研发

下图是一次生产故障演练时的告警通知

存量排查

除了对新增的白屏 case 进行观察和告警,线上存量的异常白屏场景往往是以下几类原因:

  1. 少量场景下的数据异常造成
  2. 用户特殊的操作,或代码未处理全部状态的数据,导致未能呈现正常页面

那么就需要结合上报的其他日志细致排查个例的操作轨迹,例如是否有接口数据异常、是否发生了 JS Error 等,一一排查。

思考与规划

本文探讨了一种轻量化的前端白屏监控方案。目前,该上报方案已经覆盖了货拉拉国内的所有 H5 页面,在告警系统上增加了通用的页面白屏率告警,高频页面也有初步定制化的告警规则。

通过走查上报的白屏日志,不仅发现了研发自身代码不当导致的白屏,也有业务侧的投放错误导致的问题,证实此方案确实可行。

接下来,我们的治理方案将会进一步精细化:

  1. 逐页面排查上报的案例,定位解决问题

    1. 无效的白屏上报:调整误报 case 的关注级别,根据页面路由在告警系统加白
    2. 若确认问题,在问题处理完成后,针对页面维度,重新调整告警阈值,细化监控规则以适应不同场景的规律
  2. 持续监控相关指标,防止治理成果劣化

通过跟进上述措施,相信前端白屏监控能力能有效助力提升用户体验、进一步保障系统稳定性。