前端知库

怎样检测页面白屏?常见方案有哪些误报?

白屏检测通常在加载完成后延迟检查根节点是否有内容,或用 elementFromPoint 采样视口关键点判断是否只剩容器元素,再结合错误日志和资源失败定位原因;骨架屏、慢网络、后台标签页和合法空状态都会造成误报。

参考答案

简答

  • 白屏没有浏览器原生事件,只能在约定时间点做启发式判断:根节点是否为空、视口采样点是否只有容器元素。
  • 用 document.elementFromPoint(x, y) 取视口坐标下最上层的元素,命中的全是 html、body、#app 这类容器时,该点视为空白;pointer-events: none 或不可见的元素不会被命中。
  • 判定为白屏后要附带同一会话的 JS 错误、资源加载失败和接口异常,否则只知道"白了"而不知道原因。
  • 骨架屏、加载中状态、后台标签页、慢网络和合法的空列表都会被误判,需要白名单、重试检查和可见性过滤。

根节点检查

JavaScript
function rootIsEmpty() {
  const root = document.getElementById('app');
  return !root || root.childElementCount === 0;
}

SPA 渲染失败时根节点通常保持空白,这是成本最低的信号。它的盲区是:应用渲染出了一个空壳布局或全屏错误遮罩,根节点有子元素但用户看到的仍是空白。

视口采样

JavaScript
const CONTAINERS = new Set(['HTML', 'BODY']);

function isBlankScreen(rootId = 'app') {
  const w = window.innerWidth;
  const h = window.innerHeight;
  const points = [];
  for (let i = 1; i <= 9; i += 1) {
    points.push([(w * i) / 10, h / 2]); // 横向中线
    if (i !== 5) points.push([w / 2, (h * i) / 10]); // 纵向中线,跳过已采样的中心点
  }
  const emptyCount = points.filter(([x, y]) => {
    const top = document.elementFromPoint(x, y);
    return !top || CONTAINERS.has(top.tagName) || top.id === rootId;
  }).length;
  return emptyCount === points.length;
}

采样点全部落在容器上才判定为白屏,阈值可按业务放宽(如 90%)。采样只看元素层级,不看像素,因此透明或纯白背景的大块元素会被当成"有内容"。反过来,设置了 pointer-events: none 或 visibility: hidden 的元素不会被命中,采样会穿过它落到下层。

检查时机

  • 在 load 后延迟几秒执行,留出接口和渲染时间。
  • 首次判定为空白时间隔一段再查一两次,连续空白才上报,过滤慢网络。
  • document.visibilityState === 'hidden' 时跳过,后台标签页的渲染可能被浏览器推迟。
  • 路由切换后也可以检查,SPA 的白屏常发生在懒加载分包失败时。

定位原因

白屏上报应带上下文:

常见根因是入口脚本因 CDN 故障或缓存了旧 HTML 而 404、首屏渲染抛错且没有错误边界、浏览器不支持产物使用的语法。

误报来源

  • 骨架屏和 loading 占位:把骨架屏的选择器加入"仍在加载"白名单,检查到它时延后再查。
  • 合法空状态:搜索无结果、空购物车本身就几乎是空白,应让这类页面渲染明确的空状态元素。
  • 全屏 Canvas、视频或 iframe:采样点命中的就是这些元素,需要按业务确认它们是否已绘制内容。
  • 用户很快离开:页面还没渲染完就切走,检查前先确认页面仍可见。

常见追问

  • 能用截图比像素吗?可以在合成监控或测试环境里做,线上用户端截图成本高、涉及隐私,一般不采用。
  • 白屏率怎么算?以检测到白屏的页面访问数除以完成检查的访问数,排除被跳过的后台访问。
  • 有了错误边界还需要白屏检测吗?需要,资源加载失败和入口脚本未执行时错误边界根本没有运行。