屏幕检测 🔍 前端页面白屏检测技术方案

1,844 阅读9分钟

5.png

一、前言

在现代前端应用中,页面白屏问题是一个常见且严重影响用户体验的难题。白屏通常指的是页面加载过程中,由于各种原因导致用户在一段时间内看到一个空白页面,无法看到预期的内容。这种情况可能由多种因素引起,如资源加载失败、JavaScript 执行错误、DOM 渲染问题等。对于开发者而言,及时检测并修复白屏问题对于应用的稳定性和用户的满意度来说至关重要。本文将详细介绍几种常见的前端页面白屏检测技术方案,通过对这些技术方案的深入探讨,能够启发开发者更好地应对前端页面白屏问题,提升应用的质量和用户体验。

二、白屏表现及原因

白屏指用户进入页面在加载过程中长时间无法正常展示内容,常见的表现主要有:

  1. 页面空白始终没有内容
  2. 页面仅展示骨架屏加载态
  3. 页面仅展示背景色或背景图
  4. 页面只有侧边导航菜单(常见于微前端或 iframe)

而页面白屏产生的原因通常也是多种因素导致的,常见的原因主要有:

  1. JS 异常:JS 代码存在语法错误或逻辑问题等导致页面渲染被中断无法正常展示内容;
  2. 样式问题:CSS 样式编写错误或者产生冲突导致页面布局混乱或者无法正常显示内容;
  3. 网络问题:网络环境异常导致资源无法正常下载或加载缓慢,从而导致页面无法渲染;
  4. 兼容性问题:当页面使用了某些浏览器不支持的特性时也会导致页面出现白屏的现象;
  5. 第三方服务问题:页面所依赖的三方服务或资源出现问题导致页面无法正常进行加载;

三、白屏检测

3.1 检测方案

3.1.1 可见元素识别检测

可见元素识别检测专注于检查页面上是否存在真正可见的元素内容,这是判断页面是否出现白屏现象的关键依据之一。通常情况下,该检测方法着重于定位页面中的第一个可见元素,或者依据特定需求寻找指定的元素。对于可见元素的判断,我们可以参考 LCP 性能指标的定义来确定一系列的规则,例如:

  1. 非忽略元素:一些元素因自身特性或业务需求被设定为可忽略对象我们需要剔除掉;
  2. 限定元素类型:能直接呈现内容的类型,如 text、img、video、svg、canvas、input 等;
  3. 具备实际宽高:通过 getBoundingClientRect 能够精准判断元素是否拥有实际的宽高值;
  4. 排除特定样式影响:元素样式不能存在 display: none、opacity: 0、visibility: hidden 等;

总体而言,可见元素识别检测方案在实现层面相对简单,开发成本也较低,这使得它易于被快速部署和应用。不过,从检测准确度的角度来看,该方案存在一定的局限性。对于始终处于骨架屏展示状态或加载态的页面很容易被误判为白屏;同样,对于那些仅渲染了一部分内容的页面,该检测方法也难以精准区分是正常的部分加载还是出现了白屏问题。因此,在实际应用中,我们需要结合其他检测手段,综合考量,以提升白屏检测的准确性和可靠性。

3.1.2 页面内容得分检测

火山引擎友盟等都对白屏监控采用了打分策略。友盟通过广度优先遍历白屏检测区 DOM 树上的节点来收集数据,每遇到一个可视元素则相应计分,计算 DOM 深度得分判断白屏,深度=log2(1/得分)+1,白屏得分越低越可能白屏,默认 DOM 得分少于 1.5 为极有可能白屏,1.5 到 6 之间为可能白屏,并且当检测到白屏时,会上报页面渲染的截图,以便更好地辅助问题分析或进行二次判定。页面内容得分检测方法比较依赖阈值的设定,不同页面可能情况不同,需要根据实际来进行调整,如何设置阈值是一大挑战同时如何计算得分也是一大挑战。

3.1.3 页面采样识别检测

image.webp

网易和腾讯等多个团队均采用了页面采样识别检测的方案,其主要流程如下:在获取页面宽高之后,在页面垂直/交叉位置确定多个采样点,通过 elementFromPoint 方法获取采样点的元素,然后判断采样点冒泡元素集合的第一个元素是否为容器元素、加载态元素或指定元素(结合可见元素识别检测),当存在一定比例(例如超过80%)的采样点元素为容器元素、加载态元素或指定元素时则可以判定为白屏,例如标签为body就是容器元素、Vue 根元素为#app、骨架屏容器可能有loading的类名。此外,还可以根据所有采样点的冒泡元素集合进行一一比对,判断所有采样点每组元素集合的前 n 个元素相同的比例,当比例超过一定程度的时候也可能是白屏场景,无实际有效内容渲染。

const getSamplePoints = (numSamples = 9) => {
  const points = [];
  const centerIndex = Math.floor(numSamples / 2) + 1;
  const widthPart = window.innerWidth / (numSamples + 1);
  const heightPart = window.innerHeight / (numSamples + 1);
  for (let i = 1; i <= numSamples; i++) {
    if (i === centerIndex) {
      continue; // 跳过中心点
    }
    points.push([centerIndex * widthPart, i * heightPart]); // 垂直采样
    points.push([i * widthPart, centerIndex * heightPart]); // 水平采样
    points.push([i * widthPart, i * heightPart]); // 对角线采样
    points.push([(numSamples - i + 1) * widthPart, i * heightPart]); // 反对角线采样
  }
  points.push([centerIndex * widthPart, centerIndex * heightPart]);
  return points;
};

const checkInvalidElement = (ele: Element) => {
  // 自定义无效元素规则
  if (!["BODY", "HTML"].includes(ele.tagName) && ele.id !== "app") {
    return 0;
  }
  return 1;
};

const handleDetectScreen = () => {
  // IE不兼容,直接返回
  if (!document.elementsFromPoint) {
    return;
  }
  const points = getSamplePoints();

  // 无效元素计数
  let invalidCount = 0;

  for (let i = 0; i < points.length; i++) {
    const ele = document.elementsFromPoint(points[i][0], points[i][1]);
    invalidCount += checkInvalidElement(ele[0]);
  }

  // 采样点无效元素占比80%以上,判定为白屏
  if (invalidCount / points.length > 0.8) {
    console.log("白屏");
  } else {
    console.log("非白屏");
  }
};

requestIdleCallback(handleDetectScreen);

image.png

3.1.4 其他检测

除上述目前被广泛应用的白屏检测方案之外,还有其他一些检测方案:

  1. 检测根节点是否渲染内容:Vue 和 React 等框架下 Dom 一般都挂在一个特定的根节点之下,例如 <div id="app"></div>,因此可以通过检测该根节点下是否挂载 DOM 内容或者特定元素内容来判断是否白屏。
  2. 页面截屏对比检测:将页面进行截屏,并与白色图片进行对比,当存在骨架屏的场景时可以与骨架屏图片对比,当满足一定范围的匹配的时候就认定为白屏,该方法通用性好,但是准确率低,一些仅展示背景图或显示骨架屏/加载态的场景都会被误检;
  3. 页面截屏智能检测:通过将一些人为确定的白屏和非白屏标记数据经过人工智能训练生成特定的模型,通过将页面截图进行人工智能模型来检测判定是否为白屏。
  4. 页面渲染状态检测淘宝基于 UC 内核根据 DOM 渲染状态检测白屏,采集不同阶段的页面渲染状态,通过大数据并结合端智能预判页面是否白屏。

3.2 检测时机

然而,确定可见元素的检测时机同样是一大挑战。时机把握不当,极易引发误判。若检测时机过早,可能会将页面加载过程中的短暂空白误认为是白屏,过早检测无法准确反映最终的页面呈现状态;反之,若检测时机过晚,当用户因长时间等待而关闭页面时,白屏情况将无法被及时捕捉,从而导致漏测。目前,常见的检测时机策略有以下几种:

  1. 最简单的方式就是轮询检测,但是会引入性能问题,还会产生很多无效数据;
  2. 基于 MutationObserver API 监听页面 DOM 变化,DOM 发生变化时检测白屏;
  3. 页面报错事件全局捕获,例如监听 error 事件、unhandledrejection 事件等,请结合《捕获错误 🔍 前端运行报错捕获技术方案》一起阅读;
  4. 自定义检测时机,如页面加载后 1s/2s/3s 检测白屏,可计算 1s/2s/3s 白屏率;或者路由发生变化后,等待 1s/2s/3s 后进行检测等;
  5. 页面加载完成关闭时,例如 document.readyState 在 complete 时或 load 事件触发时、beforeunload 和 unload 等事件;

同时,白屏检测可能一定程度影响性能阻塞主线程导致页面卡顿,也可以增加防抖、设置为异步调用、利用 requestIdleCallback 在浏览器空闲时进行调用等方式进行优化。

四、总结

本文主要介绍了白屏的表现形式和产生原因,并给出了几种白屏检测解决方案,包括可见元素识别检测、页面内容得分检测和页面采样识别检测等。在实际应用中,这些白屏检测方法并不是定死的,也并没有哪个方法就是最佳解决方案的,在实际应用中所有白屏检测规则和检测时机都要根据使用场景灵活调整。

参考资料

「前端监控专栏」更多内容 👇