白屏一直是一个前端开发谈之变色的问题。
“什么?我的页面刚上线就白屏了,是报错了,还是兼容性问题,还是性能问题,多刷新几次就好了,用户网络不行吧。”
简单来说,白屏就是用户打开前端页面什么有没有。
这是一个很重要的质量指标。
那么我们如何监控页面白屏异常呢?
白屏异常检测主要分为两个部分,一个是如何检测,一个是什么时候检测,
首先明确一点,页面打开慢,白屏时间长,不等于白屏;页面就是白色图,不等于白屏。
在当前SPA页面都是挂在根节点之下,通过查看关键dom是否渲染,如查看dom的高度heigt属性是否存在,如果存在,则证明关键dom已经渲染,页面不是白屏,反之,则判断页面是白屏
在上面的代码中,我们首先使用querySelectorAll方法选中了具有 .critical-node类名的关键节点。然后,通过checkNodesRendered函数检测这些节点是否已经渲染,如果有任何一个节点的高度为0,即判断为未渲染,将返回false。最后,在页面加载完成后调用checkNodesRendered函数来判断页面状态。
// 获取关键节点
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.可扩展性:示例代码可以根据实际需求进行修改和扩展,例如添加其他检测条件或特定行为。
PerformanceObserver观察FP/FCP指标,出现该指标判断为非白屏
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'] });
基于视窗坐标采集元素,如果所有元素是包裹元素,则判断是白屏
1.页面中间取17个采样点(如下图),利用 elementsFromPoint api 获取该坐标点下的 HTML 元素
2、定义属于容器元素的集合,如 ['html', 'body', '#app', '#root']
3、判断17这个采样点是否在该容器集合中。说白了,就是判断采样点有没有内容;如果没有内容,该点的 dom 元素还是容器元素,若17个采样点都没有内容则算作白屏
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('非白屏状态');
}
基于图像像素色值对比方案,白色大于阈值判断为白屏
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后开始检测
// 设置延迟时间(单位:毫秒)
const delay = 5000;
// 在延迟时间后执行检测
setTimeout(() => {
// 在这里编写检测的代码,例如调用 isWhiteScreen() 函数进行白屏检测
// 调用函数判断页面是否为白屏
const isWhite = isWhiteScreen();
if (isWhite) {
console.log("页面在加载后的5秒后出现白屏");
} else {
console.log("页面正常");
}
}, delay);
既然延迟检测时间不好定,那我们就去每秒都轮询页面,判断是否白屏。
// 设置轮询时间间隔(毫秒)
const pollInterval = 1000;
// 启动轮询检测
function startPolling() {
// 设置一个定期执行的定时器
setInterval(isWhiteScreen, pollInterval);
}
// 页面加载完成后开始轮询检测
window.addEventListener('load', startPolling);
这是一种由果索因的方案
发生白屏的原因无非以下几种
其中前两个原因占绝大多数,那么我们去监听以上错误,做白屏处理就好了。
没有最完美的方案,只有最合适的方案。
白屏方案的检测无非就是检测时机+判断方案做排列组合,找到那个投入产出比最合适的方案。
如果觉得我这篇文章写得还不错的话,
欢迎关注我的公众号:天涯碧草话斜阳,
直接搜索即可添加,我会写原创的前端文章,职场生活和成长思考。
上面有我的联系方式,如果愿意的话,可以交个朋友。
我们共同进步,一起加油!