青海网站建设公司哪家好/网络推广产品要给多少钱
前言
页面白屏,绝对是让前端开发者最为胆寒的事情,特别是随着 SPA 项目的盛行,前端白屏的情况变得更为复杂且棘手起来( 这里的白屏是指页面一直处于白屏状态 )
要是能检测到页面白屏就太棒了,开发者谁都不想成为最后一个知道自己页面白的人😥
web-see 前端监控方案,提供了 采样对比+白屏修正机制 的检测方案,兼容有骨架屏、无骨架屏这两种情况,来解决开发者的白屏之忧
知道页面白了,然后呢?
web-see 前端监控,会给每次页面访问生成一个唯一的uuid,当上报页面白屏后,开发者可以根据白屏的uuid,去监控后台查询该id下对应的代码报错、资源报错等信息,定位到具体的源码,帮助开发者快速解决白屏问题
白屏检测方案的实现流程
采样对比+白屏修正机制的主要流程:
1、页面中间取17个采样点(如下图),利用 elementsFromPoint api 获取该坐标点下的 HTML 元素
2、定义属于容器元素的集合,如
[‘html’, ‘body’, ‘#app’, ‘#root’]
3、判断17这个采样点是否在该容器集合中。说白了,就是判断采样点有没有内容;如果没有内容,该点的 dom 元素还是容器元素,若17个采样点都没有内容则算作白屏
4、若初次判断是白屏,开启轮询检测,来确保白屏检测结果的正确性,直到页面的正常渲染
采样点分布图(蓝色为采样点):
如何使用
import webSee from 'web-see';Vue.use(webSee, {dsn: 'http://localhost:8083/reportData', // 上报的地址apikey: 'project1', // 项目唯一的iduserId: '89757', // 用户idsilentWhiteScreen: true, // 开启白屏检测skeletonProject: true, // 项目是否有骨架屏whiteBoxElements: ['html', 'body', '#app', '#root'] // 白屏检测的容器列表
});
下面聊一聊具体的分析与实现
白屏检测的难点
1) 白屏原因的不确定
从问题推导现象虽然能成功,但从现象去推导问题却走不通。白屏发生时,无法和具体某个报错联系起来,也可能根本没有报错,比如关键资源还没有加载完成
导致白屏的原因,大致分两种:资源加载错误、代码执行错误
2) 前端渲染方式的多样性
前端页面渲染方式有多种,比如 客户端渲染 CSR 、服务端渲染 SSR 、静态页面生成 SSG 等,每种模式各不相同,白屏发生的情况也不尽相同
很难用一种统一的标准去判断页面是否白了
技术方案调研
如何设计出一种,在准确性、通用型、易用性等方面均表现良好的检测方案呢?
本文主要讨论 SPA 项目的白屏检测方案,包括有无骨架屏的两种情况
方案一:检测根节点是否渲染
原理很简单,在当前主流 SPA 框架下,DOM 一般挂载在一个根节点之下(比如 <div id="app"></div>
),发生白屏后通常是根节点下所有 DOM 被卸载,该方法通过检测根节点下是否挂载 DOM,若无则证明白屏
这是简单明了且有效的方案,但缺点也很明显:其一切建立在 白屏 === 根节点下 DOM 被卸载
成立的前提下,缺点是通用性较差,对于有骨架屏的情况束手无策
方案二:Mutation Observer 监听 DOM 变化
通过此 API 监听页面 DOM 变化,并告诉我们每次变化的 DOM 是被增加还是删除
但这个方案有几个缺陷
1)白屏不一定是 DOM 被卸载,也有可能是压根没渲染,且正常情况也有可能大量 DOM 被卸载
2)遇到有骨架屏的项目,若页面从始至终就没变化,一直显示骨架屏,这种情况 Mutation Observer 也束手无策
方案三:页面截图检测
这种方式是基于原生图片对比算法处理白屏检测的 web 实现
整体流程:对页面进行截图,将截图与一张纯白的图片做对比,判断两者是否足够相似
但这个方案有几个缺陷:
1、方案较为复杂,性能不高;一方面需要借助 canvas 实现前端截屏,同时需要借助复杂的算法对图片进行对比
2、通用性较差,对于有骨架屏的项目,对比的样张要由纯白的图片替换成骨架屏的截图
方案四:采样对比
该方法是对页面取关键点,进行采样对比,在准确性、易用性等方面均表现良好,也是最终采用的方案
对于有骨架屏的项目,通过对比前后获取的 dom 元素是否一致,来判断页面是否变化(这块后面专门讲解)
采样对比代码:
// 监听页面白屏
function whiteScreen() {// 页面加载完毕function onload(callback) {if (document.readyState === 'complete') {callback();} else {window.addEventListener('load', callback);}}// 定义外层容器元素的集合let containerElements = ['html', 'body', '#app', '#root'];// 容器元素个数let emptyPoints = 0;// 选中dom的名称function getSelector(element) {if (element.id) {return "#" + element.id;} else if (element.className) {// div home => div.homereturn "." + element.className.split(' ').filter(item => !!item).join('.');} else {return element.nodeName.toLowerCase();}}// 是否为容器节点function isContainer(element) {let selector = getSelector(element);if (containerElements.indexOf(selector) != -1) {emptyPoints++;}}onload(() => {// 页面加载完毕初始化for (let i = 1; i <= 9; i++) {let xElements = document.elementsFromPoint(window.innerWidth * i / 10, window.innerHeight / 2);let yElements = document.elementsFromPoint(window.innerWidth / 2, window.innerHeight * i / 10);isContainer(xElements[0]);// 中心点只计算一次if (i != 5) {isContainer(yElements[0]);}}// 17个点都是容器节点算作白屏if (emptyPoints == 17) {// 获取白屏信息console.log({status: 'error'});}}
}
白屏修正机制
若首次检测页面为白屏后,任务还没有完成,特别是手机端的项目,有可能是用户网络环境不好,关键的JS资源或接口请求还没有返回,导致的页面白屏
需要使用轮询检测,来确保白屏检测结果的正确性,直到页面的正常渲染,这就是白屏修正机制
白屏修正机制图例:
轮询代码:
// 采样对比
function sampling() {let emptyPoints = 0;……// 页面正常渲染,停止轮询if (emptyPoints != 17) {if (window.whiteLoopTimer) {clearTimeout(window.whiteLoopTimer)window.whiteLoopTimer = null}} else {// 开启轮询if (!window.whiteLoopTimer) {whiteLoop()}}// 通过轮询不断修改之前的检测结果,直到页面正常渲染console.log({status: emptyPoints == 17 ? 'error' : 'ok'});
}
// 白屏轮询
function whiteLoop() {window.whiteLoopTimer = setInterval(() => {sampling()}, 1000)
}
骨架屏
对于有骨架屏的页面,用户打开页面后,先看到骨架屏,然后再显示正常的页面,来提升用户体验;但如果页面从始至终都显示骨架屏,也算是白屏的一种
骨架屏示例:
骨架屏的原理
无论 vue 还是 react,页面内容都是挂载到根节点上。常见的骨架屏插件,就是基于这种原理,在项目打包时将骨架屏的内容直接放到 html 文件的根节点中
有骨架屏的html文件:
骨架屏的白屏检测
上面的白屏检测方案对有骨架屏的项目失灵了,虽然页面一直显示骨架屏,但判断结果页面不是白屏,不符合我们的预期
需要通过外部传参明确的告诉 SDK,该页面是不是有骨架屏,如果有骨架屏,通过对比前后获取的 dom 元素是否一致,来实现骨架屏的白屏检测
完整代码:
/*** 检测页面是否白屏* @param {function} callback - 回到函数获取检测结果* @param {boolean} skeletonProject - 页面是否有骨架屏* @param {array} whiteBoxElements - 容器列表,默认值为['html', 'body', '#app', '#root']*/
export function openWhiteScreen(callback, { skeletonProject, whiteBoxElements }) {let _whiteLoopNum = 0;let _skeletonInitList = []; // 存储初次采样点let _skeletonNowList = []; // 存储当前采样点// 项目有骨架屏if (skeletonProject) {if (document.readyState != 'complete') {sampling();}} else {// 页面加载完毕if (document.readyState === 'complete') {sampling();} else {window.addEventListener('load', sampling);}}// 选中dom点的名称function getSelector(element) {if (element.id) {return '#' + element.id;} else if (element.className) {// div home => div.homereturn ('.' + element.className.split(' ').filter(item => !!item).join('.'));} else {return element.nodeName.toLowerCase();}}// 判断采样点是否为容器节点function isContainer(element) {let selector = getSelector(element);if (skeletonProject) {_whiteLoopNum ? _skeletonNowList.push(selector) : _skeletonInitList.push(selector);}return whiteBoxElements.indexOf(selector) != -1;}// 采样对比function sampling() {let emptyPoints = 0;for (let i = 1; i <= 9; i++) {let xElements = document.elementsFromPoint((window.innerWidth * i) / 10,window.innerHeight / 2);let yElements = document.elementsFromPoint(window.innerWidth / 2,(window.innerHeight * i) / 10);if (isContainer(xElements[0])) emptyPoints++;// 中心点只计算一次if (i != 5) {if (isContainer(yElements[0])) emptyPoints++;}}// 页面正常渲染,停止轮训if (emptyPoints != 17) {if (skeletonProject) {// 第一次不比较if (!_whiteLoopNum) return openWhiteLoop();// 比较前后dom是否一致if (_skeletonNowList.join() == _skeletonInitList.join())return callback({status: 'error'});}if (window._loopTimer) {clearTimeout(window._loopTimer);window._loopTimer = null;}} else {// 开启轮训if (!window._loopTimer) {openWhiteLoop();}}// 17个点都是容器节点算作白屏callback({status: emptyPoints == 17 ? 'error' : 'ok',});}// 开启白屏轮训function openWhiteLoop() {if (window._loopTimer) return;window._loopTimer = setInterval(() => {if (skeletonProject) {_whiteLoopNum++;_skeletonNowList = [];}sampling();}, 1000);}
}
如果不通过外部传参,SDK 能否自己判断是否有骨架屏呢? 比如在页面初始的时候,根据根节点上有没有子节点来判断
因为这套检测方案需要兼容 SSR 服务端渲染的项目,对于 SSR 项目来说,浏览器获取 html 文件的根节点上已经有了 dom 元素,所以最终采用外部传参的方式来区分
总结
这套白屏检测方案是从现象推导本质,可以覆盖绝大多数 SPA 项目的应用场景
小伙们若有其他检测方案,欢迎多多讨论与交流 💕
相关文章:

前端白屏的检测方案,让你知道自己的页面白了
前言 页面白屏,绝对是让前端开发者最为胆寒的事情,特别是随着 SPA 项目的盛行,前端白屏的情况变得更为复杂且棘手起来( 这里的白屏是指页面一直处于白屏状态 ) 要是能检测到页面白屏就太棒了,开发者谁都不…...

编译原理【文法设计】—每个a后面至少一个b、ab个数相等,ab个数不相等的所有串
编译原理【文法设计】—设计每个a后面至少一个b、ab个数相等,ab个数不相等的文法为字母表Σ{a,b}Σ\{a,b\}Σ{a,b}上的下列每个语言设计一个文法 (a) 每个a后面至少有一个b的所有串 首先,每个a后面至少有一个b的正规式怎么写呢?每个a都需要…...

【死磕数据库专栏启动】在CentOS7中安装 MySQL5.7版本实战
文章目录前言实验环境一. 安装MySQL1.1 配置yum源1.2 安装之前的环境检查1.3 下载MySQL的包1.4 开始使用yum安装1.5 启动并测试二. 设置新密码并重新启动2.1 设置新密码2.2 重新登录测试总结前言 学习MySQL是一件比较枯燥的事情,学习开始之前要先安装MySQL数据库&a…...

23.2.23 22湖北省赛 B
好久没打卡了, 随便找的个水题写 这题是简单难度的 ab1 所以可以找到固定规律, 通过手动模拟可以发现 假设两种水叫做a水和b水 先倒入a水 1:0 倒入b水 1:1 此时水杯为 倒出一半的混合物, 因为ab水互溶, 比例不变 再加入a水或者b水将容器填满 比例现在变为 3:1 混合之后再…...

ONLYOFFICE中的chatGPT 是如何编写毕业论文以及翻译多种语言的
前言 chatGPT这款软件曾被多个国家的大学禁用,我们也多次在网上看到chatGPT帮助应届毕业生编写毕业答辩论文,但是这款软件目前还没有在国内正式上线,ONLYOFFICE7.3版本更新后呢,就添加了chatGPT该功能,并且正常使用。 …...

QT入门Containers之QStackedWidget
目录 一、QStackedWidget界面相关 1、布局介绍 2、插入界面 3、插入类界面 二、Demo展示 此文为作者原创,创作不易,转载请标明出处! 一、QStackedWidget界面相关 1、布局介绍 QStackedWidget这个控件在界面布局时,使用还…...

Java学习-IO流-字节缓冲流
Java学习-IO流-字节缓冲流 IO流体系↙ ↘字节流 字符流↙ ↘ ↙ ↘InputStream OutputStream Reader Writer↓ ↓ ↓ ↓ FileInputStream FileOutputStream FileRe…...

C++这么难,为什么我们还要学习C++?
前言 C 可算是一种声名在外的编程语言了。这个名声有好有坏,从好的方面讲,C 性能非常好,哪个编程语言性能好的话,总忍不住要跟 C 来单挑一下;从坏的方面讲,它是臭名昭著的复杂、难学、难用。当然ÿ…...

C#底层库--业务单据号生成器(定义规则、自动编号、流水号)
系列文章 C#底层库–MySQL数据库访问操作辅助类(推荐阅读) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/126886379 C#底层库–JSON帮助类_详细(序列化、反序列化、list、datatable) 本文链接&…...

vue3项目练习大全(附github源码)
vue慢慢的成为了前端最受欢迎的框架之一,在很多项目之中开发都能用得到,如今也已经发展到3.0了,可能是因为这个框架可以提高工作效率,因此受到大家的追捧,在之前的文章里面也说过,2019年,大前端…...

SWMM从入门到实践教程 01 SWMM软件介绍
文章目录1 软件介绍2 软件面板2.1 主菜单2.1.1文件菜单(File)2.1.2 编辑菜单(Edit)2.1.3 视图菜单(View)2.1.4 工程菜单(Project)2.1.5 报告菜单(Report)2.1.…...

CSS中的text-overflow属性详解 (控制文字在一行显示,超出部分加省略号)
text-overflow: ellipsis; 表示当文本内容超出所在容器的宽度时,用省略号来代替超出的部分。white-space:nowrap; 表示文本不换行。overflow: hidden; 表示超出容器的内容将被隐藏。 将这三个加入需要控制的css的属性中,就能控制文字在一行显示ÿ…...

基于pytorch实现模型剪枝
一,剪枝分类二,PyTorch 的剪枝三,总结参考资料一,剪枝分类 所谓模型剪枝,其实是一种从神经网络中移除"不必要"权重或偏差(weigths/bias)的模型压缩技术。关于什么参数才是“不必要的”,这是一个目前依然在研究的领域。 1.1,非结构化剪枝 非结构化剪枝(U…...

写出高质量的前端代码之消除代码中的重复
软件开发中,有个很重要的DRY原则,即Dont Repeat Yourself,也就是不要重复自己。 重复的代码会带来以下问题: 开发效率低,重复造轮子不同人开发的同一类功能,质量层次不齐修改问题时可能会遗漏,…...

怎么从零开始学黑客,黑客零基础怎么自学
很多朋友对成为黑客很感兴趣,很大原因是因为看到电影中黑客的情节觉的特别的酷,看到他们动动手指就能进入任何系统,还有很多走上黑客之路的朋友仅仅是因为自己的qq被盗了,或者游戏里的装备被别人偷了,想要自己盗回来&a…...

量化择时——资金流择时策略(第1部分—因子测算)
文章目录资金流模型概述资金流模型的有效性逻辑资金流向指标MFI(Money Flow Index)MFI指标测算测算规则测算结论资金流模型概述 通常,资金流是一种反映股票供给信息的指标,宏观上来讲,我们知道一个道理:僧…...

Openwrt中动态IPV6 防火墙的正确设置方法
环境:光猫桥接公网IPV6 问题:动态IPV6地址不知道怎么设置防火墙 解决办法:模糊匹配前缀,特定后缀 背景:将家中光猫桥接后,获得了公网的IPV6地址,可以从外部用IPV6访问家中的设备,但I…...

JS的基本数据类型和引用数据类型
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number&…...

mars3d基础项⽬常⻅报错
1.在⼤家使⽤mars3d基础项⽬的时候经常遇到这个报错,截图如下 回答: 1.原因是因为使⽤了cnpm安装依赖,导致了⼀些依赖问题 2.解决⽅式也很简答,重新使⽤ npm 或 yarn 或 pnpm安装依赖即可 2.本地加载地图时,出现报错回…...

【阿旭机器学习实战】【35】员工离职率预测---决策树与随机森林预测
【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流。 本文的主要任务是通过决策树与随机森林模型预测一个员工离职的可能性并帮助人事部门理解员工为何离职。 目录1.获取数据2.数据预处理3.分析数据3.…...

Python学习-----模块4.0(json字符串与json模块)
目录 1.json简介: 2.json对象 3.json模块 (1)json.dumps() 函数 (2)json.dumps() 函数 (3)json.loads() 函数 (4) json.load() 函数 4.总结: 1.json简介: SON(…...

open3d最大平面检测,平面分割
1.点云读入 读入文件(配套点云下载链接) # 读取点云 pcd o3d.io.read_point_cloud("point_cloud_00000.ply")配套点云颜色为白色,open3d的点云显示默认背景为白色,所以将点云颜色更改为黑色 pcd.colors o3d.utilit…...

【C++】4.类和对象(下)
1.再谈构造函数 1赋值 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;int _day; };构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次…...

自动驾驶仿真:ECU TEST 、VTD、VERISTAND连接配置
文章目录一、ECU TEST 连接配置简介二、TBC配置 test bench configuration三、TCF配置 test configuration提示:以下是本篇文章正文内容,下面案例可供参考 一、ECU TEST 连接配置简介 1、ECU TEST(简称ET),用于HIL仿…...

postgres数据库连接管理
1.连接命令psql -d postgres -h 10.0.0.51. -p 1921 -U postgres(-d指定数据库名字)2.pg防火墙介绍(pg实例层面的权限控制)pg_hba.conf文件配置文件分为5部分:配置示例#TYPE DATABASE USER ADDRESS METHODhost all loc…...

【华为OD机试模拟题】用 C++ 实现 - 环中最长子串(2023.Q1)
最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

Spring:@Async 注解和AsyncResult与CompletableFuture使用
Async概述 Spring中用Async注解标记的方法,称为异步方法,它会在调用方的当前线程之外的独立的线程中执行, 其实就相当于我们自己new Thread(()-> System.out.println("hello world !"))这样在另一个线程中去执行相应的业务逻辑…...

tidb ptca,ptcp考证
PingCAP 认证 TiDB 数据库专员 V6 考试(2023-02-23)https://learn.pingcap.com/learner/exam-market/list?categoryPCTA PingCAP 认证 TiDB 数据库管理专家(PCTP - DBA)认证考试范围指引 - ☄️ 学习与认证 - TiDB 的问答社区:lo…...

关于用windows开发遇到的各种乌龙事件之node版本管理---nvm install node之后 npm 找不到的问题
友情提醒,开发最好用nvm控制node版本 nrm 控制镜像源,能少掉很多头发开发过程中技术迭代更新的时候最要老命的就是 历史项目的node版本没有记录,导致开启旧项目的时候就会报错。尤其是npm 升级到8.x.x以后,各种版本不兼容。 真…...

JMeter做UI自动化
插件安装搜插件selenium,安装添加config添加线程组右键线程组->添加->配置元件->jpgc - Chrome Driver Configoption和proxy不解释了添加Sampler右键线程组->添加->取样器->jpgc - WebDriver Samplerscript language 选择:JavaScript&…...