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

北京中科医院是假的吗 http://m.39.net/pf/a_5131649.html

作者:一川来源:前端万有引力

1写在前面

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

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

2手动采集办法及优缺点

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

functionCScor(el,tiers,parentScore){letscore=0;consttagName=el.tagName;//判断当前的标签元素是否为指定的标签元素if(!filterTagNameInTagNames(tagName)){constchildrenLen=el.children?el.children.length:0;//判读子元素的长度是否大于0if(childrenLen0){for(letchilds=el.children,len=childrenLen-1;len=0;len--){score+=calculateScore(childs[len],tiers+1,score0)}}//判断分数是否小于等于0,且父元素的分数为0if(score=0!parentScore){if(!(el.getBoundingClintRectel.getBoundingClintRect().topWH))return0}score+=1+0.5*tiers;}returnscore}functionfilterTagNameInTagNames(tagName){return["SCRIPT","STYLE","META","HEAD"].some(tag=tag===tagName)}calFinalScore(){try{if(this.sendMark)return;consttime=Date.now()-window.performance.timing.fetchStart;letisCheckFMP=time

SCORE_ITEMSSCORE_ITEMS.length4time-(SCORE_ITEMSSCORE_ITEMS.lengthSCORE_ITEMS[SCORE_ITEMS.length-1].t

0)2*CHECK_INTERVAL

(SCORE_ITEMS.length10window.performance.timing.loadEventEnd!==0SCORE_ITEMS[SCORE_ITEMS.length-1].score===SCORE_ITEMS[SCORE_ITEMS.length-1].score);if(this.observerisCheckFMP){this.observer.disconnect//取FMP时间,默认是大于30s会自动被过滤this.fmp=recordrecord.t

try{this.checkImgs(document.body)letmax=Math.max(...this.imgs.map(element={if(/^(\/\/)/.test(element))element="


转载请注明:http://www.aierlanlan.com/tzrz/3785.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了