前端白屏检测方案

早期因为浏览器、技术、兼容性等诸多问题,导致网页的显示效果非常的单一,基本都是静态页,后续随着Angular、React、Vue等前端框架的出现,采用SPA单页面应用的方案越来越多。
用户和企业对于页面的稳定性、性能有了更高的诉求,根据Aberdeen Group的调研发现从浏览器输入地址开始访问到页面展示的最佳时间为3秒内,每多一秒的延迟会使客户满意度降低16%。Apdex(Application Performance Index)方法由 Peter Sevcik 于 2004 年首次描述。目标是有一个简单统一的开放标准来报告应用程序性能,而不管它是如何测量的。该标准将用户对应用的体验定义为三个等级,T为时间秒(s),如图1.1所示:

                                                        图1.1 等待时间和使用者情绪

所以各个公司对于自己公司应用的性能和稳定性都极为重视,有的依赖于三方提供的性能方案,国内的方案如: fundebug、岳鹰、arms,国外的产品有sentry,有的依赖于公司自己研发的方案。但是基于的原理基本都大同小异,我们这次主要谈论白屏的问题,中间也会穿插其他性能的介绍。首先,页面加载时,只有等js和css文件加载完成之后,首屏才能渲染,这就会导致出现白屏的问题,白屏是我们经常遇到的情况,白屏时间的长短影响用户对网站的第一印象,所以白屏问题很影响客户的使用体验。
近些年,公司业务高速发展,随着业务需求的增多,新兴的前端项目也是越来越多。此时对前端攻城师的交付质量有了更高的要求,性能方面是个重要的指标,白屏检测可以帮助前端人员在日常开发中预防白屏的发生,进而提升项目质量,提交客户的使用体验。下面会针对什么是白屏时间和如何检测做一个详细的介绍。

1 什么是"白屏时间"

白屏时间是指用户输入网站地址到浏览器开始显示内容的时间。当用户打开一个链接或者在浏览器输入一个地址开始访问的时候,就开始等待页面的展示,页面渲染的时间越短,用户等待的时间就越短,用户的感知越好,减少用户的跳出,这样可以极大的提升用户的体验。需要注意的是白屏并不是特指屏幕为白色,“白屏"也有可能是黑色的,重点是用户等待的时间过长,或者页面异常导致没有有效的页面,此类情况,我们都称之为"白屏”。
页面的白屏情况有以下几种:
(1)首屏加载前的白屏。
(2)页面代码崩溃导致的页面无法加载的白屏。
(3)资源代码错误导致spa无法渲染白屏。

1.1 正常"白屏"时间

1)白屏产生场景

此类白屏属于正常访问页面时产生的时间,中间涉及的为网络请求,资源加载,页面绘制等。此类正常的白屏时间是我们关注的性能指标,正常的白屏时间会给用户很大的观感体验,时间越短,用户的满意度和体验会越好。

2)白屏产生过程

白屏时间是页面展示过程中的一部分,所以就涉及到了一个老生常谈的问题———从浏览器输入地址到页面展示的过程,如图1.2所示。

                                                        图1.2 页面从url到展示过程

(1)DNS查询,域名解析,获取服务器的IP。
(2)建立TCP链接。
(3)客户端发送HTTP请求。
(4)服务器响应请求。
(5)浏览器解析并渲染页面。

前端白屏检测方案_第1张图片

                                                                图1.3 页面加载过程

如图1.3所示,我们可以很清楚的看到页面的整个加载过程,从输入url后到页面的渲染展示过程中的等待时间, 我们就视为"白屏时间",此类的白屏时间归类为正常的白屏,因为最终的结果是页面可以渲染出来的。对于这个时间的加载优化需要极为重视,因为性能问题是多种多样的。情况好点的,网站和应用程序产生一些微不足道的延迟,这些延迟会给用户一些不好的交互体验。也有及其糟糕的情况,那就是它们完全无法访问,对用户输入没有反应,或两者兼而有之。很多不利网站访问速度的因素会形成累加,从而严重影响网站的性能,导致网站访问速度变慢,用户体验低下,最终导致用户流失。

1.2 异常"白屏"

1)异常白屏产生场景

表现是浏览器无法查看有效页面,但是产生的这种情况的原因则不相同,正常的白屏为我们可以查看到页面加载完毕的结果,但是异常导致的白屏则不能,异常的白屏主要是发生在HTTP响应和浏览器渲染过程。

2)白屏原因

异常类的白屏可能是资源,或代码异常导致的页面白屏,这种情况下,我们正常检测白屏的代码可能还未加载,所以这时候无法使用正常的白屏方案检测,需要通过后端模拟浏览器访问,查看是否白屏。图1.4为通过后端检测出来的白屏,这类情况因为资源加载错误或js报错引发的"白屏",通过前端sdk无法监控到,所以需要通过后端模拟加载页面来查看是否出现"白屏"现象。

前端白屏检测方案_第2张图片

                                                                图1.4 异常资源白屏

2 白屏检测方案

2.1 首屏加载时白屏

针对首屏加载前的白屏,可以使用 Navigation Timing API 作为标准方案来作为白屏的时间。为了帮助开发者更好地衡量和改进前端页面性能,W3C性能小组引入了 Navigation Timing API ,实现了自动、精准的页面性能打点;开发者可以通过 window.performance 属性获取。Performance.timing 只读属性返回一个 PerformanceTiming 对象,这个对象包括了页面相关的性能信息。performance.navigation(定义了当前文档的导航信息,比如是重载还是向前向后等)。

1)Performance.timing浏览器兼容性

Performance.timing浏览器兼容性,如图1.5所示。

前端白屏检测方案_第3张图片

                                                        图1.5 Performance.timing兼容性介绍

2)performance.timing对应时间节点

在浏览器控制台,console输入performance可以查看到performance.timing相关时间节点m,如图1.6所示。

前端白屏检测方案_第4张图片

                                                图1.6 performance.timing参数介绍

通过navigation timing不仅可以帮助我们省去繁琐的手动打点操作,还可以帮助我们获取很多其他数据,对于整个时间节点的对应关系,如图1.7所示。

前端白屏检测方案_第5张图片

                                                图1.7 performance.timing参数说明

我们比较有用的页面性能数据大概包括如下几个:
(1)DNS查询耗时 :domainLookupEnd - domainLookupStart
(2)TCP链接耗时 :connectEnd - connectStart
(3)request请求耗时 :responseEnd - responseStart
(4)解析dom树耗时 : domComplete- domInteractive
(5)白屏时间 :responseStart - navigationStart
(6)domready时间 :domContentLoadedEventEnd - navigationStart
(7)onload时间 :loadEventEnd - navigationStart
(8)NavigationTiming的目的是用于分析页面整体性能指标。如果要获取个别资源(例如JS、图片)的性能指标,就需要使用Resource Timing API。

3)首屏加载白屏时间检测方案
A. 白屏时间收集

在上一小节,我们知道了通过performance可以计算白屏时间,白屏时间可以使用responseStart - navigationStart来计算,下面我们来实现一下主要的代码部分:

let t;
const result = {};
const _performance =
      window.performance || window.msPerformance || window.webkitPerformance;

if (_performance) {
  if (
    _performance.getEntriesByType &&
    _performance.getEntriesByType('navigation') &&
    _performance.getEntriesByType('navigation')[0]
  ) {
    t = _performance.getEntriesByType('navigation')[0];
  } else if (_performance.timing) {
    t = _performance.timing;
  }
  // 除了白屏指标,在这里我们还可以监控其他指标,比如dns,fcp,ttfb,domload等
  result.blankTime = (t.responseStart || t.domLoading) - t.navigationStart || t.fetchStart);
}

B. 数据上报

页面性能统计数据对丢失率要求比较低,并且性能统计应该在尽量不影响主流程的逻辑和页面性能的前提下进行。所以主要使用 img 标签的方式进行上报,虽然古老,但是不存在跨域的问题,基本是大家的首要选择方案。

var eventLogimg = new Image();
//  url为上报的数据内容
eventLogimg.src = url;

2.2 系统崩溃时白屏

对于崩溃式的白屏,我们一般通过检测的方式,进行排查,目前一般通过puppeteer无头浏览器进行模拟访问进行排查。
Puppeteer 是一个控制 headless Chrome 的 Node.js API 。它是一个 Node.js 库,通过 DevTools 协议提供了一个高级的 API 来控制 headless Chrome。它还可以配置为使用完整的(非 headless)Chrome。所有我们在浏览器的所有操作,都可以通过Puppeteer进行模拟和操作。
所以我们创建一个Puppeteer的服务应用,模拟访问,并对页面做白屏的计算,查看是否是白屏,简单使用的demo:

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});
  await browser.close();
})();
1)检测方案
A. 通过MutationObserver检测

MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
MutationObserver()创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。这个api可以实现对白屏的检测,具体实现原理是:
(1)侦听页面元素的变化;
(2)遍历每次新增的元素,并计算这些元素的得分总和;
(3)如果元素可见,得分为1 * weight(权重),如果元素不可见得分为0;
(4)最后根据得分判断是否是白屏。
通过前端设置项目的阀值,超过多少权重则表示为白屏,后端通过前端设置的配置,使用Puppeteer进行模拟浏览器访问,通过MutationObserver计算评分来判断是否出现白屏,如图1.8所示。

前端白屏检测方案_第6张图片

                                                                图1.8 白屏检测实现方案

相应的方法如下:
(1)disconnect()。阻止 MutationObserver 实例继续接收的通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。
(2)observe()。配置MutationObserver在DOM更改匹配给定选项时,通过其回调函数开始接收通知。
(3)takeRecords()。从MutationObserver的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord对象的新Array中。
举一个简单的例子:

// 选择需要观察变动的节点
const targetNode = document.getElementById('observation-id');
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
    // Use traditional 'for loops' for IE 11
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(targetNode, config);
// 之后,可停止观察
observer.disconnect();

>注意:因为我们是通过Puppeteer模拟浏览器访问,所以不存在MutationObserver的浏览器兼容问题。

B. 通过判断某个区域的元素是否存在

通过document.elementsFromPoint,进行取点,该函数返回还在特定坐标点下的HTML元素数组。在页面的onload事件后,获取界面十字线上的点,然后可以获取到里面的元素,因为这个能获取最里面的元素,所以就通过这个进行判断,如果排除html,body等容器标签,仍有标签存在,那就不是空白点,否则是空白点,根据业务需要设定空白点比值,大于这个值就是白屏,例如:垂直方向取10点个,水平方向取10个点,如果最后空白点的数量大于18个,代表页面白屏,进行页面白屏的报警触发。

// 检测白屏
const checkBlank = function () {
    if (!document.elementsFromPoint) {
        return;
    }
    // 排除body和html标签
    const excludeElements = ['body', 'html']
    // 计算画的多少无内容的标签
    let nothingElement = 0
    // 计算画的区域一共多少标签
    let totalElement = 0
    const getElements = (el) => {
        if (!el) return ''
        return (el.classList && el.classList[0]) || el.id || el.localName
    }
    const isWrap = (el) => {
        if (!el) return;
        totalElement++
        if (excludeElements.indexOf(getElements(el)) >= 0) {
            nothingElement++
        }
    }
    let elementsX, elementsY;
    // 画区域,检测区域内标签
    for (let i = 1; i < 10; i++) {
        elementsX = document.elementsFromPoint(window.innerWidth * i / 10, window.innerHeight / 2)
        elementsY = document.elementsFromPoint(window.innerWidth / 2, window.innerHeight * i / 10)
        isWrap(elementsX[0])
        isWrap(elementsY[0])
    }
    // 判断是否白屏
    if (totalElement - nothingElement < 2) {
        return true;
    }
    return false;
};
C. 通过图像识别

图像识别可以通过对页面访问时截取的图片做识别,查看图片是否为白屏。但是这个方案并不能检测所有的白屏,因为并不是所有的异常都会以白色的方式进行显示。

2)方案对比

方案\对比项 MutationObserver elementsFromPoint 图像识别
检测白屏 可以监控 可以监控 可以监控部分情境下白屏
性能 需要检测多个元素点 通过设置区域或关键点检测元素 通过图片识别
优势 通过权重的方式计算各个元素的占比,计算方式更合理 通过画点计算,性能更快 优势不明显
劣势 使用者需要针对不同的页面做详细的元素以及权重的设置,使用体验不好 可能某些区域没有足够的元素去检测导致白屏误报,所有也需要针对每个页面做白屏点设置 无法检测所有情况下的白屏

3 白屏检测方案实践

3.1 整体设计

我们的目标是对于正常情况下的白屏做汇总,通过汇总的表格内容查看,对于异常情况下的白屏需要做预警,当发现有异常的白屏,通过手机、短信等方式进行预警。所以我们最需要的是主动预警,这样我们就可以放心的睡觉了。当系统出现异常,系统马上通知我们,我们通过详情快速定位问题,修复bug,如图1.9所示。

                                                        图1.9 白屏检测告警模块

整体设计方案,如图1.10所示。

前端白屏检测方案_第7张图片

                                                        图1.10 白屏检测整体设计方案

用户接入sdk,并在管理系统设置项目信息、预警信息、白屏检测页面等配置。在正常白屏产生的情况下,sdk上报性能数据到日志中心,定时任务定时拉取日志中心的数据并做聚合汇总后,数据保存到数据库中,用户可以通过项目管理查看项目性能指标。对于异常的白屏内容,白屏服务定时检测页面,判断是否有白屏情况的产生,如果有白屏的情况产生则通过预警机制进行预警。用户收到预警信息后,可以在白屏检测系统查看白屏检测详情。

3.2 方案选择

正常的白屏就使用performance的属性来检测,对于异常的白屏,针对这三种方案,我们最终选择使用判断某个区域的元素个数来判断是否白屏。具体方案为垂直方向取20点个,水平方向取20个点,如果最后空白点的数量大于某个数值,代表页面白屏,通过取多个点来降低白屏检测错误率。
难点问题,因为很多页面为需要登陆才可以检测的页面,我们无法直接检测这样的页面,所以需要解决登陆的问题,在管理页面提供了用户填写鉴权方式功能,使用者根据自己项目的实际使用,设置字段和值,如图1.11所示。

前端白屏检测方案_第8张图片

                                                        图1.11 解决登录问题方案

通过白屏的定时检测,可以查看白屏检测的汇总结果,如图1.12所示,可以查看异常和正常的数量,通过详情,可以查看每个页面白屏检测的详情,如图1.13所示。

前端白屏检测方案_第9张图片

                                                               图1.12 白屏检测结果

前端白屏检测方案_第10张图片

图1.13 白屏检测结果详情

4 总结

页面性能差的话会非常影响用户体验,但是在使用过程中,页面性能差的情况并不少见。白屏是个重要的性能指标,我们的目标是记录所有的白屏情况,需要按照两种情况进行汇总,一个是正常的白屏情况,通过请求上报汇总,进行展示,可以通过上报的各个指标查看项目的详情。针对不同的情况做项目的优化。第二种是异常情况下的白屏,通过无头浏览器模拟访问项目页面,通过上面的方式检测页面是否白屏,并做记录和汇总,如果出现错误则进行预警。
白屏检测可以帮助我们详细的掌握我们项目的质量以及是否影响用户体验,同时在出现问题的时候可以及时收到预警反馈。在白屏检测中除了白屏时间的显示还有页面性能,dom加载时间以及是否有缓存,有无缓存的对比加载,有助于我们专注我们的项目已经优化项,可以把错误和不良体验扼杀在摇篮里。
在开发过程中,如何让页面更快更好更稳定的运行,是区分一个前端攻城狮专业水平和视野的重要指标,作为前端工程师最核心的价值或者责任就是将大伙所有努力的结果最终完美的呈现给客户,所以我们需要重视这些性能指标。通过对页面性能的提升,带给客户更好的产品体验,这样才会得到好的产品反馈,给企业带来价值。

参考资料

【1】https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/timing
【2】https://www.w3.org/TR/navigation-timing/
【3】https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
【4】https://www.w3.org/TR/DOM-Level-3-Events/
【5】https://www.apdex.org/?spm=a2c4g.11186623.2.8.7a0b5c7cSIyNEd

你可能感兴趣的:(前端)