怎样检测页面白屏?常见方案有哪些误报?
白屏检测通常在加载完成后延迟检查根节点是否有内容,或用 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 的白屏常发生在懒加载分包失败时。
定位原因
白屏上报应带上下文:
- 最近的 JS 错误和未处理 Promise 拒绝,采集方式见《前端怎样全局捕获 JS 错误、Promise 拒绝和资源加载失败?》。
- 脚本或 CSS 加载失败,尤其是 CDN 上的入口文件与懒加载分包。
- 关键接口的状态码和耗时。
- 页面 URL、发布版本、浏览器版本。
常见根因是入口脚本因 CDN 故障或缓存了旧 HTML 而 404、首屏渲染抛错且没有错误边界、浏览器不支持产物使用的语法。
误报来源
- 骨架屏和 loading 占位:把骨架屏的选择器加入"仍在加载"白名单,检查到它时延后再查。
- 合法空状态:搜索无结果、空购物车本身就几乎是空白,应让这类页面渲染明确的空状态元素。
- 全屏 Canvas、视频或 iframe:采样点命中的就是这些元素,需要按业务确认它们是否已绘制内容。
- 用户很快离开:页面还没渲染完就切走,检查前先确认页面仍可见。
常见追问
- 能用截图比像素吗?可以在合成监控或测试环境里做,线上用户端截图成本高、涉及隐私,一般不采用。
- 白屏率怎么算?以检测到白屏的页面访问数除以完成检查的访问数,排除被跳过的后台访问。
- 有了错误边界还需要白屏检测吗?需要,资源加载失败和入口脚本未执行时错误边界根本没有运行。