# 白屏监控

白屏通常指的是页面打开后,浏览器上面的地址栏已经显示完整的 URL,但是页面内容无法渲染,只有白色的空白页面。

导致白屏的原因大致可分为两类:

  • 资源加载问题
  • 代码执行错误

从现代前端视角来看,这两种原因都跟当前SPA框架的广泛使用有关。

# 资源加载问题

这里的资源特指 JavaScript 脚本、样式表、图片等静态资源,不包括接口调用等动态资源。

资源加载问题导致的白屏可以分为可恢复的和不可恢复的两大类

# 可恢复的白屏

可恢复的白屏常见于第一次进人页面时,由于资源加载过慢或者接口请求未返回,所以浏览器无法执行下一步骤。

这种白屏通常是网络状况太差或者设备性能太差等原因导致的,一般在浏览器返回后,就能恢复页面渲染,可以通过监控首屏时间来发现。

TIP

如果生产环境的首屏时间呈异常上升趋势,那么一定是页面白屏时间过长导致的,开发人员应该及时关注并排查近期改动的代码。

# 不可恢复的白屏

不可恢复的白屏大多是由于网络或缓存问题导致的。

常见的例子莫过于 React、Vue 等 SPA 框架构建的 Web 应用,一旦 [bundle|app].js 因为网络原因访问失败,便会引发页面白屏。

TIP

你可以访问任意SPA框架构建的Web应用,按照如下步骤复现:打开 DevTools > Network,找到 app.xxx.js,右键并选中 Block request URL,随后刷新页面。

另一个例子是SPA项目打包后,在非首次线上替换dist文件时,某些手机/浏览器在之后首次打开页面,可能出现白屏情况

在用户端会默认缓存index.html入口文件,而由于vue打包生成的css/js都是哈希值,跟上次的文件名都不同,因此会出现找不到css/js的情况,导致白屏的产生。在服务端更新包之后,由于旧的文件被删除,而index.html所链接的路径依然是旧文件路径,因此会找不到文件,从而白屏。

# 代码执行错误

代码执行错误导致的白屏一般伴随着功能流程的阻断出现,并且很难通过等待或者页面刷新等方法修复。

这类问题出现的原因通常是前端代码逻辑错误,或是后端接口的脏数据导致的前端数据解析逻辑错误,最终导致运行异常的前端代码触发了页面崩溃。

例如,如果 React 中的组件发生了异常,并且外部没有使用 componentDidCatch 或者getDerivedStateFromError 捕错误,那么 React 组件 render 挂载的目标节点下的 DOM 树会被移除、页面就会出现白屏。

自React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。

# 白屏检测

# 检测根节点是否渲染

这种方法的原理是在当前主流 SPA 框架下,DOM 一般挂载在一个根节点之下(比如 <div id="app"></div> ),发生白屏后通常是根节点下所有 DOM 被卸载。

我们可以通过在页面渲染完成后,检查页面根节点的子元素是否存在,如果不存在,则可以判断页面是白屏状态。

这种方案简洁明了,但缺点也很明显,项目有骨架屏渲染的情况下无法判断是否白屏。

如在 Vue.js 中可以使用钩子函数 mounted 或 created 在页面渲染完成后进行检测。

export default {
  created() {
    // 获取页面根节点
    const rootNode = document.querySelector('#app');
    // 检测根节点的子元素是否存在
    if (rootNode.children.length === 0) {
      // 页面是白屏状态
      console.error('页面白屏了!');
    } else {
      // 页面正常显示
      console.log('页面正常显示');
    }
  },
}
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14

TIP

由于 SPA 应用通常使用异步加载方式加载组件和数据,因此需要确保在页面组件渲染完成后再进行白屏检测。如果在组件渲染之前进行检测,可能会误判为白屏。另外,SPA 应用可能会存在某些异步请求失败或加载超时等问题,也需要考虑这些因素对白屏检测的影响。

# Mutation Observer 监听 DOM 变化

Mutation Observer 是一种在 DOM 树发生变化时进行回调的方式,可以用于监听页面元素的变化。可以通过 Mutation Observer 来监听 DOM 树变化,从而判断页面是否白屏。

但这个方式的缺点也很明显:

  • 对性能的影响:使用 Mutation Observer 监听 DOM 变化会对浏览器性能造成一定影响,尤其是当 DOM 树变化频繁时,会导致回调函数频繁执行,影响页面的响应速度和用户体验。
  • 兼容性问题:Mutation Observer 的兼容性不是很好,不同浏览器支持程度不同,需要进行兼容性处理。
  • 误判问题:有些情况下,页面并不是真正的白屏,但是 Mutation Observer 仍然会误判为白屏,例如页面中有一个空白的 div 元素占位,此时即使页面内容未加载,也不会被判定为白屏状态。

具体实现步骤如下:

  1. 创建一个 Mutation Observer 实例,并指定回调函数。
const observer = new MutationObserver((mutations) => {
  // mutations 表示 DOM 树的变化列表
});
成功
1
2
3
  1. 将 Mutation Observer 实例与根节点进行绑定,并指定监测的选项。
const rootNode = document.documentElement;
const observerConfig = {
  childList: true, // 监听子节点的变化
  subtree: true, // 监听所有后代节点的变化
  attributes: true, // 监听节点属性的变化
  characterData: true, // 监听节点文本内容的变化
  attributeOldValue: true, // 记录节点属性变化前的值
  characterDataOldValue: true, // 记录节点文本内容变化前的值
};
observer.observe(rootNode, observerConfig);
成功
1
2
3
4
5
6
7
8
9
10
  1. 在回调函数中检查 DOM 树的变化,如果发现根节点的子元素存在,则可以判断页面不是白屏状态。
const observer = new MutationObserver((mutations) => {
  // 检查根节点的子元素是否存在
  if (rootNode.children.length > 0) {
    console.log('页面正常显示');
  } else {
    console.error('页面白屏了!');
  }
});
成功
1
2
3
4
5
6
7
8

# 页面截图

通过对网页进行截图,对截图进行像素点分析,判断页面是否白屏。一般情况下,可以统计截图中所有像素点的 RGB 值,如果所有像素点的 RGB 值都相同,且与白色(RGB(255, 255, 255))非常接近,那么就可以判断页面是白屏状态。

这个方式的缺点如下:

  • 页面截图需要包含足够的像素点,以便能够准确地检测页面是否白屏。如果截图不够清晰,可能会导致误判。
  • 有些情况下,页面并不是真正的白屏,但是由于一些外部原因(例如网络问题)导致页面未加载,此时页面截图也会被判定为白屏状态。
  • 页面存在骨架屏的情况下,需要对比骨架屏