前言
白屏一直是一个前端开发谈之变色的问题。
“什么?我的页面刚上线就白屏了,是报错了,还是兼容性问题,还是性能问题,多刷新几次就好了,用户网络不行吧。”
简单来说,白屏就是用户打开前端页面什么有没有。
这是一个很重要的质量指标。
那么我们如何监控页面白屏异常呢?
白屏异常检测主要分为两个部分,一个是如何检测,一个是什么时候检测,
检测方案
首先明确一点,页面打开慢,白屏时间长,不等于白屏;页面就是白色图,不等于白屏。
关键节点是否渲染
在当前SPA页面都是挂在根节点之下,通过查看关键dom是否渲染,如查看dom的高度heigt属性是否存在,如果存在,则证明关键dom已经渲染,页面不是白屏,反之,则判断页面是白屏
实现思路
在上面的代码中,我们首先使用querySelectorAll方法选中了具有 .critical-node类名的关键节点。然后,通过checkNodesRendered函数检测这些节点是否已经渲染,如果有任何一个节点的高度为0,即判断为未渲染,将返回false。最后,在页面加载完成后调用checkNodesRendered函数来判断页面状态。
```js // 获取关键节点 const criticalNodes = Array.from(document.querySelectorAll('.critical-node'));
// 检测节点渲染 function checkNodesRendered() { let allNodesRendered = true;
for (const node of criticalNodes) { if (node.offsetHeight === 0 || node.clientHeight === 0) { allNodesRendered = false; break; } }
return allNodesRendered; }
// 判断页面状态 if (checkNodesRendered()) { // 关键节点已经渲染,页面不是白屏 console.log("页面不是白屏"); // 可以进行后续操作 } else { // 关键节点未渲染,页面是白屏 console.log("页面是白屏"); // 可以进行相应处理 }
// 在页面加载完成后调用检测函数 window.addEventListener('load', checkNodesRendered); ```
优点
1.简单易懂:代码相对简洁,易于理解和实现。
2.快速检测:代码通过检测关键节点的渲染状态来快速判断页面是否为白屏,方便进行后续处理。
3.可扩展性:示例代码可以根据实际需求进行修改和扩展,例如添加其他检测条件或特定行为。
缺点
- 局限性:示例代码仅仅关注关键节点是否渲染,但并不能涵盖所有可能的页面白屏情况。
- 不适用于异步加载:如果页面中的关键节点是通过异步加载或延迟加载的方式渲染的,示例代码可能无法正确判断页面状态。
- 可能的误判:某些情况下,即使关键节点已经渲染,它们的高度可能仍为0。这可能导致误判,将页面错误地视为白屏。
观察FP/FCP
PerformanceObserver观察FP/FCP指标,出现该指标判断为非白屏
代码实现
```js const observer = new PerformanceObserver((list) => { const entries = list.getEntries(); entries.forEach(entry => { if (entry.name === 'first-paint') { // 处理FP指标 console.log('First Paint:', entry.startTime); // 进行白屏判断 if (/* 根据需求判断是否为白屏 /) { console.log('白屏!'); } } else if (entry.name === 'first-contentful-paint') { // 处理FCP指标 console.log('First Contentful Paint:', entry.startTime); // 进行白屏判断 if (/ 根据需求判断是否为白屏 */) { console.log('白屏!'); } } }); });
observer.observe({ entryTypes: ['paint'] }); ```
优点
- 通过观察FP和FCP指标,可以精确地确定页面加载过程中是否出现白屏,以及白屏持续的时间。这对于优化网页加载速度和用户体验非常有帮助。
- PerformanceObserver提供了一个直接的、标准化的接口来监测性能指标,使开发者能够更方便地收集和分析网页性能数据。
缺点
- 依赖于浏览器对PerformanceObserver的支持,在某些旧版本或不常见的浏览器中可能无法正常工作。
- 只通过FP和FCP来判断白屏可能不够全面,因为白屏可能涉及其他因素,如网络延迟、脚本执行等。因此,需要结合其他性能指标和实际场景来综合评估页面的加载情况。
基于视窗坐标采集法
基于视窗坐标采集元素,如果所有元素是包裹元素,则判断是白屏
1.页面中间取17个采样点(如下图),利用 elementsFromPoint api 获取该坐标点下的 HTML 元素
2、定义属于容器元素的集合,如 ['html', 'body', '#app', '#root']
3、判断17这个采样点是否在该容器集合中。说白了,就是判断采样点有没有内容;如果没有内容,该点的 dom 元素还是容器元素,若17个采样点都没有内容则算作白屏
代码实现
```js const samplePoints = [ { x: 100, y: 100 }, // 示例采样点1 { x: 200, y: 200 }, // 示例采样点2 // 添加更多采样点... ];
const containerElements = ['html', 'body', '#app', '#root']; // 定义容器元素集合
function hasContentAtSamplePoints() { for (const point of samplePoints) { const elements = document.elementsFromPoint(point.x, point.y); const hasContent = elements.some(element => !isContainerElement(element)); if (!hasContent) { return false; // 该采样点没有内容 } } return true; // 所有采样点都有内容 }
function isContainerElement(element) { // 判断元素是否属于容器元素集合的逻辑,例如根据元素的标签名或选择器进行判断 return containerElements.includes(element.tagName.toLowerCase()) || containerElements.includes(element.id); }
// 调用函数判断是否存在白屏状态 const isWhiteScreen = !hasContentAtSamplePoints();
if (isWhiteScreen) { console.log('白屏状态'); } else { console.log('非白屏状态'); } ```
优点
- 快速确定白屏状态:通过采样点的方式,可以快速检查页面中是否存在白屏状态,而无需遍历整个页面。
- 简单实现:实现起来相对简单,只需要使用 elementsFromPoint API 获取元素并进行判断。
缺点
- 采样点数量和位置选择:在示例中,我们选择了固定数量和位置的采样点,但这可能并不能涵盖所有情况。正确选择采样点的数量和位置是必要的,以保证准确性和可靠性。
- 容器元素定义的准确性:需要准确定义容器元素集合,以确保正确判断哪些元素属于容器元素。容器元素集合的定义可能会因页面结构变化而需要定期更新维护。
- 采样点是否具有代表性:通过采样点判断白屏状态,需要确保采样点能够代表页面的关键区域和内容。如果关键区域未覆盖到,或者采样点无法代表页面的典型情况,可能会导致误判。
图像检测
基于图像像素色值对比方案,白色大于阈值判断为白屏
代码实现
```js function isWhiteScreen(imageData) { const threshold = 200; const pixels = imageData.data; const length = pixels.length;
for (let i = 0; i < length; i += 4) { const red = pixels[i]; const green = pixels[i + 1]; const blue = pixels[i + 2]; const alpha = pixels[i + 3];
// 将 RGB 转换为灰度值
const grayscale = (red + green + blue) / 3;
// 如果灰度值低于阈值,则返回 false
if (grayscale < threshold) {
return false;
}
}
// 如果所有像素的灰度值都高于阈值,则返回 true return true; }
// 获取页面截图,可以通过其他方式获取 imageData const imageData = ...;
// 调用函数判断页面是否为白屏 const isWhite = isWhiteScreen(imageData);
if (isWhite) { console.log("页面出现白屏"); } else { console.log("页面正常"); } ```
优点
- 具有广泛适用性:该方法可以适用于各种类型的网页和应用程序,不受页面结构和布局的限制。
- 准确性较高:通过对页面截图进行像素色值对比,可以较为准确地判断页面是否呈现白色,避免了部分误判的可能性。
缺点
- 截图准确性:该方法的准确性依赖于页面截图的质量和准确性。如果截图质量较低或者不准确,则可能导致判断结果不准确。
- 阈值选择:选择合适的阈值是关键。过高的阈值可能导致漏判,而过低的阈值可能导致误判。阈值的选择应该根据具体情况和实际测试进行调整。
- 页面动态性:对于动态页面或存在异步加载内容的页面,截图时可能无法捕获到完全加载的状态,从而导致判断结果不准确。
- 效率问题:对整个页面进行截图并处理像素色值对比可能会消耗较多的计算资源和时间,特别是对于复杂的页面或者移动端设备
检测时机
其实检测方案并不难,难的是什么时候检测。
这里介绍三种方案。
延迟检测
通过设定延迟时间(如5s),在页面加载后的5s后开始检测
代码实现
```js // 设置延迟时间(单位:毫秒) const delay = 5000;
// 在延迟时间后执行检测 setTimeout(() => { // 在这里编写检测的代码,例如调用 isWhiteScreen() 函数进行白屏检测 // 调用函数判断页面是否为白屏 const isWhite = isWhiteScreen();
if (isWhite) { console.log("页面在加载后的5秒后出现白屏"); } else { console.log("页面正常"); } }, delay); ```
缺点
- 固定延迟时间:使用固定的延迟时间可能不适用于所有情况。页面加载时间的变化、网络速度的差异等因素可能导致延迟时间不准确,有可能延迟过长或过短。
- 不适用于快速加载的页面:如果您的页面加载速度很快,在延迟时间之内已经完成加载并呈现内容,延迟检测可能会错过白屏状态。
- 无法应对动态内容:如果页面内容是动态加载的,延迟检测可能在页面加载完成后立即触发,此时页面尚未呈现完全。
轮询检测
既然延迟检测时间不好定,那我们就去每秒都轮询页面,判断是否白屏。
代码实现
```js // 设置轮询时间间隔(毫秒) const pollInterval = 1000;
// 启动轮询检测 function startPolling() { // 设置一个定期执行的定时器 setInterval(isWhiteScreen, pollInterval); }
// 页面加载完成后开始轮询检测 window.addEventListener('load', startPolling); ```
缺点
- 资源消耗:频繁的轮询检测可能会增加浏览器的资源消耗,包括 CPU 和内存。这可能对性能产生一定的影响,特别是在较低性能的设备或者页面加载较慢的情况下。
- 不准确性:轮询检测往往基于时间间隔来判断页面加载状态,而不是依赖于实际的视觉变化。这可能导致在某些情况下误判页面加载完成,或者延迟较长时间才判断出白屏状态。
- 反应迟钝:由于轮询需要等待一定的时间间隔才能进行下一次检测,因此可能会导致对白屏状态的响应有一定的延迟。这对于需要快速捕捉白屏问题的场景可能不太理想。
错误监听
这是一种由果索因的方案
发生白屏的原因无非以下几种
- 脚本错误:当页面中的 JavaScript 代码存在错误时,可能导致页面渲染中断,进而出现白屏情况。常见的错误包括语法错误、逻辑错误、资源加载错误等。
- 网络问题:如果页面所需的资源(如样式表、脚本、图片等)无法正确加载,或者网络连接不稳定,可能导致页面无法正确渲染,最终呈现为白屏。这种情况下,可能还会出现超时错误或网络请求失败的错误。
- HTML结构错误
- 样式问题
- 见兼容性问题。
其中前两个原因占绝大多数,那么我们去监听以上错误,做白屏处理就好了。
优点:
- 简单易实现:通过监听错误事件,可以比较简单地实现白屏检测逻辑。
- 可靠性较高:当页面发生未捕获的错误时,通常表明页面加载或解析出现了问题,可能导致白屏情况。
缺点:
- 性能开销:错误处理函数可能会对页面性能产生一定的影响,尤其是在页面发生多个错误时。因此,需要注意错误处理逻辑的优化,避免性能问题。
总结
没有最完美的方案,只有最合适的方案。
白屏方案的检测无非就是检测时机+判断方案做排列组合,找到那个投入产出比最合适的方案。
如果觉得我这篇文章写得还不错的话,
欢迎关注我的公众号:天涯碧草话斜阳,
直接搜索即可添加,我会写原创的前端文章,职场生活和成长思考。
上面有我的联系方式,如果愿意的话,可以交个朋友。
我们共同进步,一起加油!