服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - 编程技术 - 前端性能优化笔记之首屏时间采集指标的具体方法

前端性能优化笔记之首屏时间采集指标的具体方法

2022-01-12 23:47前端万有引力一川 编程技术

通常,我们在开发环境中进行首屏时间测试,是通过在内网中通过Chrome DevTools观察首屏时间,本篇文章主要对前端性能优化笔记之首屏时间采集指标的具体方法进行简要说明,具有一定的参考价值,下面跟着小编一起来看下吧...

前端性能优化笔记之首屏时间采集指标的具体方法

1写在前面

通常,我们在开发环境中进行首屏时间测试,是通过在内网中通过Chrome DevTools观察首屏时间,这样内外网络环境存在差异,导致测量的首屏时间也会有所不同。我们在开发中使用的是调试工具,而用户是直接访问的,两者的访问形式是不同的。观察首屏时间的设备有多种,而真实的用户人群不同,移动设备的型号和所处网络环境也是各异的。

那么,如何了解用户的首屏时间呢?大量用户的首屏时间分布又是怎样的呢?性能差的用户首屏时间又是多少呢?

2手动采集办法及优缺点

所谓手动采集,一般就是通过埋点的方式进行采集上报,如:我们要收集当前页面的用户停留时间,就必须采集到打开页面的时间和关闭或隐藏页面的时间,再进行计算得到停留时间并上报。

如果是电商列表页面,瀑布流型的页面,需要根据各个机型的首屏位置,估算出一个平均的首屏位置,然后进行打点上报。

手动采集的兼容性强,可以随着情况而进行变动,其次可以去中心化,各个业务模块单独负责自己的打点代码,有问题时业务程序员去排查问题即可。但是手动采集也存在一些问题,容易与业务代码严重耦合,它的覆盖率不足,业务程序员一旦忙起来,性能优化方案的实施就会延迟排后。

3自动化采集的办法及优点

自动化采集,即引入一段通用的代码来做首屏时间自动化采集,引入过程中,除了必要的配置外不需要做其他事情。独立性强,接入过程更加自动化,可以由一个公共团队来开发,试点后进行推广到各个业务团队。但是,有些个性化需求是无法得到满足的,因为在工作中总会遇到一些特殊业务场景,会遇到难以实施自动化采集的情况。

4服务端模板业务下的采集方案

有人会说现在的前端开发不都是采用web框架进行开发吗,为啥还会涉及到服务器模板呢。那是因为在一些B端业务的公司用的还是服务端模板,如Velocity、Smarty等,比如说微前端框架SSR也是用的服务端模板。

之所以会出现这种情况,这是因为后端比较重、前端偏配合,出于效率考虑前后端并没有进行解耦。这时候如果使用现在流行的web前端框架vue/react,这无疑就会增加学习成本。

前端性能优化笔记之首屏时间采集指标的具体方法

使用浏览器提供的DOMContentLoaded接口来采集首屏时间点,具体的思路是:当页面中的HTML元素被加载和解析完成后,DOMContentLoaded事件会被触发,首屏时间=DOMContentLoaded时间=DOMContentLoadedEventEnd-fetchStart时间。

当然这种采集方法不能用于SPA单页面应用业务场景,这是因为在使用Performance API接口采集的首屏时间可能是1106ms。而实际首屏时间可能就是1976ms。在SPA单页面中,用户请求一个页面时,页面会先加载index.html,加载完成后就会触发DOMContentLoaded和load。页面会相关脚本资源并通过axios异步请求数据,使用数据渲染页面主题部分,这个时候首屏才渲染完成。SPA的流行让Performance API接口失去了原先的意义,那么,这种情况下应该如何采集首屏指标呢?

当然,我们的解决方案是采用MutationObeserver采集首屏时间。

5单页面SPA应用业务场景下的采集方法

如果一个首屏页面的内容没有被组件化,那么首屏时间就无法被统计到,除非各个业务都制定一套组件标准,首屏内容必须封装成组件。前面也知道onload的时间也并非最终时间,可能在onlaod阶段,首屏还没加载完。其次,没有考虑到首屏是张图片的情况,首屏虽然加载完成了,但是图片是异步的,图片并没有进行加载。

我们想如果能够在首屏渲染过程中,把各个资源的加载时间记录到日志中,后续再通过分析,确定某个资源加载完的时间,那么就是首屏时间。

MutationObeserver接口提供了监督对DOM树所做更改的能力,它被设计为旧的MutationEvents功能的替代品,该功能是DOM3 Events规范的一部分。

当用户进入页面时,我们可以使用MutationObeserver监控DOM元素,当DOM元素发生变化时,程序会标记变化的元素,记录时间点和分数,储存在数组中。首屏指标采集到某些条件时,首屏渲染已经结束了,我们需要考虑到首屏采集终止的条件。

递归遍历DOM元素及其子元素,根据子元素所在层级设定元素权重,比如:页面DOM元素的第一层设置为1,当其被渲染时得分为1,每增加一个元素层级权重增加0.5,当第五层级元素的权重就为3.5,渲染时给出对应分数。根据前面统计到的元素层级得分,计算元素的分数变化率,获取变化率最大点对应的分数,然后找到该分数对应的时间,即为首屏时间。

  1. function CScor(el, tiers, parentScore){ 
  2.   let score = 0; 
  3.   const tagName = el.tagName; 
  4.   // 判断当前的标签元素是否为指定的标签元素 
  5.   if(!filterTagNameInTagNames(tagName)){ 
  6.     const childrenLen = el.children ? el.children.length : 0; 
  7.     // 判读子元素的长度是否大于0 
  8.     if(childrenLen>0){ 
  9.       for(let childs = el.children, len = childrenLen-1; len >= 0; len--){ 
  10.         score += calculateScore(childs[len],tiers+1,score>0) 
  11.       } 
  12.     } 
  13.     // 判断分数是否小于等于0,且父元素的分数为0 
  14.     if(score<= 0&& !parentScore){ 
  15.       if(!(el.getBoundingClintRect&& el.getBoundingClintRect().top<WH)) return 0 
  16.     } 
  17.     score += 1 + 0.5 * tiers; 
  18.   } 
  19.   return score 
  20.  
  21. function filterTagNameInTagNames(tagName){ 
  22.   return ["SCRIPT","STYLE","META","HEAD"].some(tag=>tag===tagName) 
  23.  
  24. calFinalScore(){ 
  25.   try { 
  26.     if(this.sendMark) return
  27.     const time = Date.now() - window.performance.timing.fetchStart; 
  28.     let isCheckFMP = time > 30000 || SCORE_ITEMS && SCORE_ITEMS.length > 4 && time - (SCORE_ITEMS && SCORE_ITEMS.length && SCORE_ITEMS[SCORE_ITEMS.length-1].t || 0) > 2 * CHECK_INTERVAL || ( 
  29.       SCORE_ITEMS.length > 10 && window.performance.timing.loadEventEnd !== 0 && 
  30.       SCORE_ITEMS[SCORE_ITEMS.length-1].score === SCORE_ITEMS[SCORE_ITEMS.length - 1].score 
  31.     ); 
  32.     if(this.observer && isCheckFMP){ 
  33.       this.observer.disconnect 
  34.       // 取FMP时间,默认是30001大于30s会自动被过滤 
  35.       this.fmp = record && record.t || 30001 
  36.       try { 
  37.         this.checkImgs(document.body) 
  38.         let max = Math.max(...this.imgs.map(element=>{ 
  39.           if(/^(\/\/)/.test(element)) element = "https:" + element 
  40.           try { 
  41.             return window.performance.getEntriesByName(element)[0].responseEnd || 0 
  42.           } catch (error) { 
  43.             return 0  
  44.           } 
  45.         })) 
  46.       } catch (error) { 
  47.         return 
  48.       } 
  49.     } 
  50.   } catch (error) { 
  51.     return 
  52.   } 

如果页面里包括图片,使用上面的首屏指标采集方案,结果准确吗?答案是不准确的。上述的计算逻辑主要针对的是DOM元素而做的,图片加载过程是异步,图片容器(图片的DOM元素)和内容的加载是分开的,当容器加载出来时,内容还没出来,一定要确保内容加载出来,才算是首屏。

进行个归纳,通常计算首屏时间的方法有:

  • 首屏模块标记法
  • 统计首屏内加载最慢的图片
  • 自定义首屏

首屏模块标签标记法

在首屏模块标签标记法中,首屏时间等于firstScreen - performance.timing.navigationStart;。但是在实际业务中,能够使用首屏模块标签标记法的情况比较少,大多数页面都需要通过接口拉取数据才能完整展示,因此我们会使用JavaScript 脚本来判断首屏页面内容加载情况。

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.   <meta charset="UTF-8"
  5.   <title>首屏</title> 
  6.   <script type="text/javascript"
  7.     window.pageStartTime = Date.now(); 
  8.   </script> 
  9.   <link rel="stylesheet" href="common.css"
  10.   <link rel="stylesheet" href="page.css"
  11. </head> 
  12. <body> 
  13.   <!-- 首屏可见模块1 --> 
  14.   <div class="module-1"></div> 
  15.   <!-- 首屏可见模块2 --> 
  16.   <div class="module-2"></div> 
  17.   <script type="text/javascript"
  18.     window.firstScreen = Date.now(); 
  19.   </script> 
  20.   <!-- 首屏不可见模块3 --> 
  21.   <div class="module-3"></div> 
  22.     <!-- 首屏不可见模块4 --> 
  23.   <div class="module-4"></div> 
  24. </body> 
  25. </html> 

 

统计首屏内图片完成加载的时间

在实际进行首屏加载中,加载最慢的资源文件是图片,对此我们可以将加载最慢的图片文件的时间作为首屏时间。这是因为在浏览器发起HTTP请求,在页面中建立TCP连接,但是每个页面所能建立的连接数又是有限的,使得并不能一次性将所有的图片都能进行下载和展示。

基于此种情况,我们可以在页面DOM树构建完成后去遍历首屏内所有的图片标签,并对每个图片标签的onload事件进行监听,从而计算得到所有图片中加载时间的最大值。这样就得到首屏时间=加载最慢的图片的时间点 - performance.timing.navigationStart。

自定义模块内容计算法

由于在统计首屏内遍历图片标签列表得到最大加载时间比较复杂,对此在业务中可以通过自定义模块内容,来简化计算首屏时间。如下面的做法:

忽略图片等资源加载情况,只考虑页面主要DOM

只考虑首屏的主要模块,而不是严格意义首屏线以上的所有内容

6参考文章

《前端性能优化方法与实践》

《前端优化-如何计算白屏和首屏时间》

7写在最后

本文主要介绍了首屏指标采集相关的内容,这种性能采集方案靠谱吗?当前的互联网大厂又在使用什么采集方案呢?就目前而言,上面介绍的是当前应用的最好的首屏指标采集方案,兼容了单页面应用和服务端模板的页面。

原文链接:https://mp.weixin.qq.com/s/6bf8kgzCt3akAiZJYiAFNw

延伸 · 阅读

精彩推荐