彻底掌握页面白屏检测

news2024/10/7 12:18:10

前言

在日常的前端开发中,白屏几乎是每个前端开发者都会遇到的问题。白屏问题严重影响了用户体验。当用户访问一个页面时,如果页面长时间处于白屏状态,用户可能会认为页面出现了问题,从而选择离开。这对于任何一个网站都是不利的,尤其是对于那些依赖用户流量的电商网站来说,白屏问题可能直接导致用户流失和收入下降。

许多研究都表明,用户最满意的打开网页时间,是在2秒以下。 用户能够忍受的最长等待时间的中位数,在6~8秒之间。 这就是说,8秒是一个临界值,如果你的网站打开速度在8秒以上,那么很可能,大部分访问者最终都会离你而去。

白屏的主要原因

白屏的主要原因可以从浏览器渲染的整个流程来分析,我们知道从一次请求到页面渲染大致会经过以下过程

用户输入URL -> DNS解析 -> 建立连接 -> 返回内容 -> 浏览器解析响应内容 -> 浏览器渲染

其中任何一环出现问题都有可能造成页面白屏,下面是一些最常见的白屏原因:

1. 资源加载失败页面依赖的关键资源(CSS、JS、图片等)加载失败,导致页面无法正常渲染。
//例如我们在React项目中引入了不存在的js资源或者资源加载失败,就会造成页面无法正常渲染,导致白屏。
import './index.js'
function App() {
  return (
    <div className="App">
     app
    </div>
  )
}
export default App

这种情况下我们可以在关键的接口和请求上增加响应监控。

2. 资源加载延迟资源加载延迟(或阻塞),导致页面长时间等待资源加载完成。出现空白。
//例如我们在这里,App 组件在挂载后会使用 setTimeout 模拟资源加载延迟 8 秒。  
import { useState, useEffect } from 'react'
import Preview from 'preview'
function App() {
  const [data, setData] = useState(null)
  useEffect(() => {
    setTimeout(() => {
      setData({ name: 'test' })
    }, 10000)  // 模拟资源加载 10000 秒
  }, [])
  return (
    <div className="App">
      {data && <Preview />}
    </div>
  )
}
export default App

这种情况可以前端做一些loading状态或者增加骨架屏,防止页面无内容造成用户困扰。

3. 代码执行中出现未被捕捉的错误,例如JavaScript执行错误,Promise错误等等。导致页面功能无法正常工作,出现空白。

这个错误也是前端最最最常见的一个错误,如果没有处理好异常情况导致抛出错误有没有被捕获,就会造成页面崩溃。

function App() {
  return (
    <div className="App">
      {
        JSON.parse(data.name) // 未对data.name判空导致parse解析失败
     	}
    </div>
  )
}
export default App
4. 浏览器兼容问题

不同的浏览器对于前端技术的支持程度不同,如果我们使用了浏览器不支持的语法或者CSS类型,可能导致某些浏览器无法正常显示页面。

function App() {
  return (
    <div className="App">
      {data?.name} // 例如浏览器不支持可选链式符就会报错, https://caniuse.com/?search=%3F.
    </div>
  )
}
export default App

除此之外,第三方服务或资源出现问题,浏览器缓存、CDN问题等也会导致页面白屏的问题。在实际开发中我们需要考虑到各种可能的情况逐步排查。

如何监测页面白屏

要检测白屏,主线的思路就是检测在特定时间特定的DOM是否存在于页面中。或者是通过页面截图并且识别图片是否包含内容等方式(代价太高)。下面就给大家分享几种常用的方式,最后也会给出一种最通用的方式。

1. 全局错误监听 + 判断对应节点是否存在

例如在React中我们可以使用ErrorBoundary实现由于代码执行错误导致白屏的检测步骤:

  1. 首先,创建一个名为 ErrorBoundary 的新组件。在这个组件中,我们需要定义一个名为 componentDidCatch 的生命周期方法,它将在子组件中捕获到错误时被调用。同时,我们需要在组件的状态中存储一个表示是否发生错误的变量。
import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // 在这里可以将错误记录到日志,或者发送给后端
    console.log(error, info);
    this.setState({ hasError: true });
    if (!document.getElementById('container').innerHTML) {
      console.log('页面白屏了');
    }
  }

  render() {
    // if (this.state.hasError) {
    //   // 如果发生错误,显示备用 UI
    //   return <h1>出错了,请刷新页面或联系客服。</h1>;
    // }

    // 如果没有发生错误,正常渲染子组件
    return this.props.children;
  }
}

export default ErrorBoundary;

  1. 接下来,将 ErrorBoundary 组件包裹在应用程序的根组件中,以便捕获整个应用程序的错误。
const App = () => {
  return <div>{a?.sjakd}</div>
}

import React from 'react';
import ReactDOM from 'react-dom';
import ErrorBoundary from './ErrorBoundary';

ReactDOM.render(
  <React.StrictMode>
    <ErrorBoundary>
      <App />
    </ErrorBoundary>
  </React.StrictMode>,
  document.getElementById('root')
);

现在,如果应用程序中的任何子组件发生错误,ErrorBoundary 组件将捕获该错误,并判断对应的根节点是否函数对应的节点来实现白屏检测。

2. Mutation Observer 监听 DOM 变化

MutationObserver API 可以用于监测DOM树的变化。我们可以通过MutationObserver来监测页面的DOM变化,从而判断页面是否出现白屏现象。

class App extends React.Component {
  componentDidMount() {
    const observer = new MutationObserver((mutations, observer) => {
      const duration = Date.now() - observer.startTime;

      if (duration > 3000) {
        console.log('页面白屏时间超过3秒');
      }
    });

    observer.startTime = Date.now();
    observer.observe(document.body, { childList: true, subtree: true });
  }

  // ...
}

以React框架为例,我们可以在componentDidMount生命周期函数中使用MutationObserver API 获取相关信息。如果两次渲染时间过长说明出现了白屏的情况。

但是这种方法对下面这两种情况没有很好的解决办法

1)DOM根本就没有渲染

2)遇到有骨架屏的项目,若页面从始至终就没变化,一直显示骨架屏,这种情况 Mutation Observer 也束手无策

3. 关键点采样对比

所谓关键点采样就是在我们的屏幕中,随机取几个固定的点,利用document.elementsFromPoint(x,y)该函数返还在特定坐标点下的 HTML 元素数组。这也是准确率比较高的一种做法,目前主流的都是这种检测方法。具体实现如下:

1、页面中间取n个采样点,利用 elementsFromPoint api 获取该坐标点下的 HTML 元素。采样方法有垂直选取交叉选取,以及垂直交叉选取三种方法,对应的采样图片如下:

image-20240703153111902 image-20240703153128412 image-20240703153150175

2、定义属于容器元素的集合,如 ['html', 'body', '#app', '#root']

3、判断这n个采样点是否在该容器集合中。目的就是为了判断采样点有没有内容;如果没有内容,那么我们获取到的 dom 元素就是容器元素,若n个采样点都没有内容则可判定为白屏

4、若初次判断是白屏,开启轮询检测,来确保白屏检测结果的正确性,直到页面的正常渲染或者重试一定次数就关闭定时器

具体代码实现如下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<script>
  function whiteScreenMonitor(wrapperSelectors) {
  // 记录空白点数
  let emptyPoints = 0;
  let timer = null;
  const maxRetryTime = 5;
  let retryTime = 0;

  // 获取元素的选择器
  function getSelector(element) {
    if (element?.id) {
      return `#${element.id}`;
    } if (element?.className) {
      return (
        `.${
          element.className
            .split(' ')
            .filter(item => !!item)
            .join('.')}`
      );
    }
    return element?.nodeName.toLowerCase();
  }

  // 判断元素是否为包裹元素
  function isWrapper(element) {
    const selector = getSelector(element);
    if (wrapperSelectors.indexOf(selector) !== -1) {
      return true;
    }
  }

  function checkBlankScreen() {
    // 检查屏幕的点,分别为横向和纵向的点
    for (let i = 1; i <= 9; i++) {
      // 获取当前点的所有元素
      const xElements = document.elementsFromPoint(
        (window.innerWidth * i) / 10,
        window.innerHeight / 2,
      );
      const yElements = document.elementsFromPoint(
        window.innerWidth / 2,
        (window.innerHeight * i) / 10,
      );

      // 取第一个来判断
      emptyPoints += !!isWrapper(xElements[0]);
      emptyPoints += !!isWrapper(yElements[0]);
    }

    // 如果空白点数超过16个,表示屏幕为空白
    if (emptyPoints > 16) {
      console.log(`这是第 ${retryTime} 次检测,页面白屏了`);
      if (++retryTime > maxRetryTime) {
        console.log('页面白屏检测超过最大次数,可判定为白屏');
        // 这里可以做一些监控上报之类的事情
        clearTimeout(timer);
        return;
      }
      if (!timer) {
        timer = setInterval(() => {
          emptyPoints = 0;
          checkBlankScreen();
        }, 1000);
      }
    } else {
      clearTimeout(timer);
    }
  }
  window.addEventListener('load', checkBlankScreen);
}

// 开始检测白屏,如果是SDK则可以将这个当法导出
whiteScreenMonitor(['html', 'body', '#container', '.content', '#app', '#root']);
</script>
<body>
<div id="root"></div>
</body>
<script>
  setTimeout(() => {
    const content = document.createElement('div')
    content.style.width = '500px'
    content.style.height = '500px'
    content.style.backgroundColor = 'red'
    document.getElementById('root').appendChild(content) // 挂载
  }, 10000); // 模拟白屏的操作
</script>
</html>

这里使用初次检测 + 轮训防止误判的方式来进行白屏检测。关键内容在函数checkBlankScreen中取点并判断内容是容器节点的个数。

image-20240703155810525

使用了骨架屏的页面如何检测白屏

对于有骨架屏的页面,用户打开页面后,先看到骨架屏,然后再显示正常的页面,来提升用户体验;但如果页面从始至终都显示骨架屏,也算是白屏的一种

骨架屏示例:https://ant-design.antgroup.com/components/skeleton-cn

检测骨架屏的白屏的方式其实也很简单,我们可以稍微改改上面的方法即可:

  1. 传入骨架屏对应的容器给whiteScreenMonitor函数,这样检测的时候就可以将骨架屏的内容过滤掉。
  2. 改变检测白屏的方式,我们通过每次获取到的内容和第一次获取到的内容进行对比,如果每一次都相同说明是白屏的。但是这里就需要保证我们的白屏检测代码一定要是最先运行的,不然等我们所有的内容都渲染完了再检测,这是后内容也是没有改变的。

总结

以上就是常用的白屏检测方法了,相信看完大家以后对于如何检测项目中白屏有了自己的看法。

最后打个广告,我新开了个公众号,旨在将自己日常学习的内容进行沉淀。这个公众号会经常更新前端相关的技术文章,还请大家多多支持,点点关注💗。

image-20240703162104337

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1889485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么要在成像应用中使用图像采集卡?

达到最大产量是工业和工厂自动化的关键标准之一。提高传感器分辨率和帧速率有助于实现这一目标&#xff0c;但也使带宽达到极限&#xff0c;并提出了新的传输问题。当前高带宽接口(如10GigE、相机直接与PC连接和嵌入式系统)的实现促使成像应用的许多用户询问如何以最佳配置最优…

idea部署war包成功,但是接口404

场景 项目结构 xxx-xxx-app xxx-xxx-service xxx-xxx-webappapp/webapp依赖service&#xff0c;service中写了各种api&#xff0c;先别管它合不合理&#xff0c;正式环境用webapp发布。 本地配置tomcat启动&#xff0c;但是发现每次部署成功&#xff0c;但是service中的接口…

常见的九种二极管

常见的九种二极管 文章目录 常见的九种二极管1、普通二极管2、光电二极管&#xff08;LED&#xff09;3、变容二级管4、发光二极管5、恒流二极管6、快恢复二极管&#xff08;FRD&#xff09;7、肖特基二极管8、瞬态电压抑制二极管(TVS)9、齐纳二极管&#xff08;稳压&#xff0…

声音音频文件波谱可视化展示

1、简单图形展示 import matplotlib.pyplot as plt import numpy as np import torch import torchaudiodef plot_waveform(waveform, sample_rate, title"Waveform", xlimNone, ylimNone):waveform waveform.numpy()num_channels, num_frames waveform.shapetime…

基于用户的协同过滤算法

目录 原理&#xff1a; 计算相似度&#xff1a; 步骤&#xff1a; 计算方法&#xff1a;Jaccard相似系数、余弦相似度。 推荐 原理&#xff1a; 先“找到相似用户”&#xff0c;再“找到他们喜欢的物品”--->人以群分。即&#xff0c;给用户推荐“和他兴趣相似的其他用…

selenium,在元素块下查找条件元素

def get_norms_ele_text(self):elementsself.get_norms_elements()locBy.CSS_SELECTOR,"div.sku-select-row-label"by loc[0] # 获取By类型&#xff0c;例如By.CSS_SELECTORvalue loc[1] # 获取具体的CSS选择器字符串&#xff0c;例如"div.sku-select-row-l…

【C++】vector的底层原理及实现

文章目录 vector的底层结构迭代器容量操作size()capacity()reserve()resize() 默认成员函数构造无参构造函数带参构造函数 析构拷贝构造赋值重载 operator[ ]插入删除操作insert()任意位置插入erase()任意位置删除push_back()尾插pop_back()尾删 vector的底层结构 我们的目的不…

数据万象推出智能检索MetaInsight,现已开启限时公测

海量文件的分析统计一直是对象存储COS的热点需求&#xff0c;伴随AIGC飞速迭代发展&#xff0c;在众多不同模态素材的海洋中&#xff0c;用户也急需更高效地管理和利用多媒体内容&#xff0c;打破传统搜索的桎梏。 数据万象推出的智能检索 MetaInsight 服务将多模态检索与元数…

第十四章 Qt绘图

目录 一、Qt绘图基础 1、主要的类 2、paintEvent 事件 二、坐标体系 三、画笔 1、画笔的常用接口 2、画笔样式 3、画笔画线时的端点样式 4、画笔画线时,连接点的样式 5、实例 四、画刷 1、画刷的填充样式 2、实例 五、基本图形的绘制 1、画矩形 drawRect 2、画…

miniconda3 安装jupyter notebook并配置网络访问

由于服务器安装的miniconda3&#xff0c;无jupyter notebook&#xff0c;所以手工安装jupyter notebook 1 先conda 安装相关包 在base 环境下 conda install ipython conda install jupyter notebook 2 生成配置文件 jupyter notebook --generate-config Writing defaul…

Coze终于顶不住了?开始收费了

&#x1f914;各位老铁都知道&#xff0c;之前Coze以免费出圈&#xff0c;香碰碰&#xff0c;字节一个月几个亿补贴用户。现在终于顶不住了&#xff0c;开始收费了&#xff01; 我们来看看具体情况吧&#xff01; &#x1f4b8;收费情况一览 目前国内版本还没有开始收费&#x…

CesiumJS【Basic】- #054 绘制渐变填充多边形(Entity方式)-使用shader

文章目录 绘制渐变填充多边形(Entity方式)-使用shader1 目标2 代码2.1 main.ts绘制渐变填充多边形(Entity方式)-使用shader 1 目标 使用Entity方式绘制绘制渐变填充多边形 - 使用shader 2 代码 2.1 main.ts import * as Cesium from cesium;const viewer = new Cesium…

迅睿CMS 后端配置项没有正常加载,上传插件不能正常使用

首先&#xff0c;尝试迅睿CMS官方提供的【百度编辑器问题汇总】解决方案来解决你的问题。你可以访问这个链接&#xff1a;官方解决方案。 如果按照【百度编辑器问题汇总】解决方案操作后&#xff0c;依然遇到“后端配置项没有正常加载&#xff0c;上传插件不能正常使用”的问题…

windows重装系统

一、下载Ventoy工具&#xff0c;制作启动盘 官网地址&#xff1a;https://www.ventoy.net/cn/download.html 电脑插入用来制作系统盘的U盘&#xff0c;建议大小在8G以上。 双击打开刚解压出来的Ventoy2Disk.exe文件。打开界面如图&#xff1a; 确认U盘&#xff0c;如图&am…

Linux_管道通信

目录 一、匿名管道 1、介绍进程间通信 2、理解管道 3、管道通信 4、用户角度看匿名管道 5、内核角度看匿名管道 6、代码实现匿名管道 6.1 创建子进程 6.2 实现通信 7、匿名管道阻塞情况 8、匿名管道的读写原子性 二、命名管道 1、命名管道 1.1 命名管道通信 …

VoiceCraft—— 业界最高水平的自然语音合成语言模型

VoiceCraft: 实现语音编辑和合成的 SOTA 论文地址&#xff1a;https://arxiv.org/html/2403.16973v1 源码地址&#xff1a;https://github.com/jasonppy/voicecraft 本文介绍VoiceCraft 的开发情况&#xff0c;它在语音编辑和零点语音合成 (TTS) 方面都实现了 SOTA。在本文中…

如何压缩jpg图片的大小?关于缩小jpg图片的四种方法

如何压缩jpg图片的大小&#xff1f;压缩JPG图片大小是一项常见的技术&#xff0c;用来优化图片以适应不同的应用需求。无论是为了在网页上提高加载速度、减少存储空间占用&#xff0c;还是为了便于通过电子邮件或社交媒体分享&#xff0c;压缩jpg图片都是必不可少的步骤。这种技…

AIoTedge:智能边缘计算平台

随着物联网(IoT)和人工智能(AI)技术的飞速发展&#xff0c;AIoT&#xff08;人工智能物联网&#xff09;已成为推动智能化转型的关键力量。AIoT Edge作为这一领域的创新平台&#xff0c;通过边缘计算技术&#xff0c;为企业提供了一个高效、灵活且安全的解决方案。 边云协同架构…

Java 7新特性深度解析:提升效率与功能

文章目录 Java 7新特性深度解析&#xff1a;提升效率与功能一、Switch中添加对String类型的支持二、数字字面量的改进三、异常处理&#xff08;捕获多个异常&#xff09;四、增强泛型推断五、NIO2.0&#xff08;AIO&#xff09;新IO的支持六、SR292与InvokeDynamic七、Path接口…

Xilinx FPGA:vivado实现乒乓缓存

一、项目要求 1、用两个伪双端口的RAM实现缓存 2、先写buffer1&#xff0c;再写buffer2 &#xff0c;在读buffer1的同时写buffer2&#xff0c;在读buffer2的同时写buffer1。 3、写端口50M时钟&#xff0c;写入16个8bit 的数据&#xff0c;读出时钟25M&#xff0c;读出8个16…