H5的白屏检测方案实践

7,270 阅读5分钟

1.背景

页面“白屏”是项目代码异常引起的,需要特别关注。故希望对该场景进行精准的探测,并由此而来开发出一整套白屏检测模块。

2.问题分析

2.1 白屏定义

白屏,是页面出于某些异常原因,未进行渲染,导致页面阻塞在白屏状态。

2.2 白屏检测职责边界

页面加载异常通常表现为白屏;但有时也会表现为页面加载不完全、或页面展示错乱等;

我们本次希望实现无侵入业务的通用白屏检测模块,故仅检测白屏case;后续异常可以在业务方进行关注。

2.3 技术方案调研

2.3.1 白屏感知手段

如何利用技术手段检测当前页面是否白屏,是本模块的重点。

业界也存在一些经典的白屏检测手段,梳理如下。

  • 检测页面关键DOM的是否渲染

  • 通用的DOM渲染监听

  • H5截图(canvas绘图)检测

  • native截图(容器截屏)检测

  • 利用performance.getEntries("paint")获取fp/fcp来感知渲染

其中,各方案的优缺点也比较明显,简述如下:

1.基于dom的检测,优点是简单;缺点是通用型较差,缺乏业界认可的检测算法;

2.H5截图:优点是准确;缺点是需要借助canvas,性能有影响;

3.native截图:缺点是需要借助容器能力;

4.利用浏览器performance API:缺点是兼容性不够好;

2.3.2 检测时间点选取

关于判定时间点的选取,尚未在网络上查找到相关的成熟技术方案或文献。第一小节调研了白屏检测的技术方案,接下来还需要调研白屏检测工作的时间点;

3.方案设计

3.1 白屏感知方案

基于2.3.1小节的技术方案调研结果,我们希望设计一套综合性能较高的白屏检测方案,能够在准确性、通用型、易用性等方面均表现良好。

故我们选择方案1,需要接入方经过简单的业务评估,输入关键DOM信息和相关检测配置项,即可在项目中开启白屏检测。

1)业务方通过配置项传入关键DOM的选择器信息;

2)每次检测时,通过DOM选择器进行查询;如检测存在该DOM节点,检查其渲染height是否存在;

3)若存在关键DOM未渲染,则判定为白屏。

3.2 检测时间点选定方案

3.2.1 难点描述

图1

如图1所示,我们需要确定白屏判定时间点T1,但T1的时间点选定是一个难点,因为它可能会影响白屏判定的准确性。

1)如时间点过早,可能会将加载缓慢case误判为白屏;

2)如时间点过晚,可能由于白屏已经迫使用户手动关闭页面,导致漏判

所以难点出现了:如何确定检测时间点T1呢?

3.2.2 白屏修正机制

这里,我们创造性的提出了“白屏修正机制”,来解决如上这个两难的问题,具体机制描述如下:

第一步:将T1调整至时间轴原点T0;

这样就解决 T1过晚导致错失检测时机 问题

那读者会问了:T1提前至T0后,会将所有加载case认定为白屏,这种误判如何规避呢?

第二步:我们提出概念“白屏修正”,

它是指在T1之后,继续探测页面渲染情况;一旦页面发生渲染,则上报事件信息;

最终,从统计数据上,实际白屏数目 = T1白屏发生数 - T2 页面渲染数;

这样就同时解决1和2两个问题。具体流程下图2。

图2

3.2.3 参数优化

细心的读者可能会发现,基于白屏修正机制的检测方案,依然存在问题。

上图图2可见,如果用户在T1至T2之间,手动退出,则会导致正常渲染的case未被修正,会发生一些误判

针对这种问题,我们考虑从统计学角度触发,重新权衡T1时间点的设定。

具体示意图如下图图3,通过获取项目的页面渲染耗时均值(建议采取T90),该时间点标记为T3;我们将T1酌情向T3靠近;

理论上T1约接近T3,误判率就越低。

图3

3.2.4 调参的一些建议

细心的读者可能还会发现,在3.2.3小节介绍的参数调节工作中,T1向T3靠近,就又产生了3.2.1节第二点讨论过的漏判问题。问题好像陷入了一个循环?

其实问题的本质是:用户会随机关闭页面,这个关闭的时间点无法预知。

这会导致无论如何调整T1点,都会产生部分漏判部分误判,无法得到最优解。本方案是在寻找次优解的路上探索。

因此,我们建议业务方将T1向T3靠近的过程中,酌情调整:

调整的效果是:

T1越靠左,则容易误判白屏; T1越靠右,则容易漏判白屏。

调参的建议是:

简单H5页面,加载速度快,建议将T1靠近T0点,优先避免漏判;

复杂H5页面,加载速度慢,建议将T1靠近T3点,优先避免误判;

4.方案实现

4.1 白屏感知

_isWhiteScreen() {
    const domQueryList = this.keyDom; // keyDom样例: ['.geo-btn']
    const domList = domQueryList
      .map(domQuery => {
        return document.querySelector(domQuery);
      })
      .filter(dom => !!dom);
    const keyDomAllHit = domList.length > 0 && domList
      .every(dom => {
        return dom && dom.getBoundingClientRect().height > 10;
      });
    return !keyDomAllHit;
  }

4.2 白屏探测流程

// 基本参数配置
const THRESHOLD_TIME = 5; // 判定时间点 单位:秒
const END_TIME = 30; // 轮训结束时间 单位:秒
const INTERVAL_TIME = 1; // 轮询间隔 单位:秒

// 建立轮询器,重复进行白屏探测
setInterval(() => {
  if (页面渲染) {
    if ( 已经判定过白屏 ) 上报白屏修正"whiteScreenHappen-Correction";
    clearInterval();
  } else if ( time = T1 )  { 
    上报白屏“whiteScreenHappen”; // T1:白屏检测的时间点
    continue;
  } else 
    continue;
  }
}, 1*1000);