专栏首页前端杂货铺关于首屏时间采集自动化的解决方案

关于首屏时间采集自动化的解决方案

关于首屏

首屏时间是指从转向该页面到屏幕中该页面所有内容都可见时的时间。已经有太多的关于首屏时间的计算,在本文中并不重复阐述这些已经被提出或者实现的方案,而旨在探索与讨论更多的首屏自动化采集方案,扩大思考范围,你我思想之间互相碰撞往往可以激起更多的稀奇古怪的解决方案,这也正是我写这篇文章的目的。

通过浏览器调试工具,我们可以清晰的看出页面资源加载时序图:

先是html页面加载,token进行词法、语法解析后开始加载静态资源并执行相关脚本,开始构建DOM树、render树和CSSOM数,最后加载图片,用户看到完整的网页。

虽然浏览器有着各自的优化的解决方案,但是大多数情况下图片往往是最后加载完毕,这不仅仅是由于图片的大小相对较大,而且图片的加载与否与DOM结构有着很大的关系。DOM是否构建完毕,render树中是否渲染以及其他的图片加载策略有关系可能都会影响图片加载时序。因此在首屏时间的计算中,我们是以最终首屏图片的加载时间为节点计算的。

首屏计算

原则1 首屏计算模块不应该耦合业务线

一般而言,首屏计算作为一个抽离出的js脚本单独引用,这个模块尽量不暴露API给开发者使用,所有的采集端任务都由该模块完成。这句话可能听起来像一句废话,但还是有很多情况可能需要业务人员来进行首屏渲染时间的判断的,下面将针对这个情形举一个实际的场景:

随着MVVM模式的兴起,前端异步渲染逐渐流行起来,前端编码逐渐由面向jQuery编程转向为面向Vue编程。可是使用Vue编写的业务代码在本地打包后仅仅是一个bundle,此时的HTML文件中只是一个

的占位符而已,那么首屏时间计算模块该如何准确的计算首屏时间呢?因此首屏时间计算模块必须知道首屏的DOM结构渲染完毕的时间节点,在这个节点时刻进行计算首屏范围内的图片加载时间。 可是如何获取首屏DOM结构渲染完毕的时间节点呢?这就需要业务开发人员制定。在更新vue实例的data属性后,通知首屏计算模块此时DOM接口已渲染完毕,开始计算首屏时间。

MVVM开发模式下,首屏时间的计算已经耦合了业务代码,虽然可以在保证首屏时间的准确性,但却给开发者带来了一些可观判断逻辑,而这些判断往往会困扰新入职的同志们,因此我们的目标之一就是解决需要手动打点进行首屏时间计算的现状。

原则2 性能与准确性的权衡

业界有个通过canvas截屏并通过轮询对比不同时间点截屏图片之间某几个随机像素点,从而判断首屏是否加载完毕。这种方式虽然科学,但是估计没有几个公司会采用这种方案。通过canvas截屏这个操作对硬件的要求可能就比较高,而且需要进行额外的像素运算,因此性能肯定很差。其实这种场景在工程领域经常出现,工程不同于科学那般严谨,我们只需要找到给定条件的最优解即可,做工程也就是在做trade off。因此这种对比方案我们也必须摒弃。

实现

再次强调,由开发者打点首屏DOM渲染完毕进行首屏时间计算的方式是相对准确的方式,因此我们后续讨论的自动化计算首屏时间的准确性都是基于此标准进行对比说明,因为自动化计算肯定是没有人工干预准确的,这一点毫无疑问。

轮训采集大法

仍然是轮训,不同的是在每次轮询中执行一些操作:

  1. 获取首屏的所有图片(包括IMG标签与css相关属性)
  2. 绑定首屏图片的onload和onerror事件,每次轮询不会重复绑定已绑定的图片
  3. 相同图片不需重复绑定事件侦听,否则会与 2 中的每次轮询混淆
  4. 图片的事件处理函数执行打点信息并统计图片加载状态,同时比对时间戳得到最迟加载的时间

具体的实现中,需要特别注意首屏出现的相同图片的情况。笔者起初在获取首屏图片中简单计算图片的url数组,存储重复图片的个数,并且与该图片的加载状态绑定在一起。如首屏中出现了3张相同的图片,那么在该图片onload或onerror中对已加载图片的数量做 加3 处理,否则导致最终的 已加载图片总数 与 首屏图片总数 不相等的情况发生。这种实现导致逻辑非常的差,且实现复杂。后通过存储图片所在的DOM对象数组实现更为简单的图片状态判断,更加已读。

伪代码如下:

// totalCounter为轮询的总时间
// DemandCounter是规定的轮询总时间,为3000ms
// imgsLoadedCount则为首屏已加载的图片数量
// lastImageLoadedStamp为最后加载的图片时间戳
function checkFirstScreenDomReady(){
    if(totalCounter >= DemandCounter){
        // ...
        var stamps = Object.keys(pools),
        len = stamps.length,
        i = 0,
        it; 
        finalImgCount = pools[stamps[len - 1]].imgLen; 
        pollEnd = true;

        for(;i<len;i++){
            it = pools[stamps[i]];
            if(it.imgLen == finalImgCount && it.imgsLoadedCount >= finalImgCount){
                self.onRecord = true;
                _perfQueue._firstScreenLoadEnd = lastImageLoadedStamp;
                firstScreen.firstScreenLoadEnd = lastImageLoadedStamp;
                firstScreen.duaring = lastImageLoadedStamp - performance.timing.navigationStart;
                
                reportData(firstScreen);
                return;
            }
        }
        return;
    }
    
    var imgEls = getImage();

    imgEls.forEach(function(el) {
        if(!imgLoadedHash.get(el)){
            var img = new Image();
            imgLoadedHash.put(el,{
                loaded: true,
            });
            img.onload = OnLoad;
            img.onerror = OnError;
            img.src = el.__src;
        }
    });
    pools[totalCounter+''] = {
        imgLen: imgEls.length,
        stamp: Date.now(),
        imgsLoadedCount: imgsLoadedCount
    };
    totalCounter += timeout;
}

watch dog采集

利用Mutation Observer API进行侦听 内容框的DOM事件,判断首屏DOM结构是否完备;如果构建完毕则侦听首屏范围内的图片加载事件,计算首屏时间。

watch dog需要知晓合适首屏DOM构建完毕。这需要首屏计算模块主动插入一个打点标签

,将业务代码放置在标签内部(这个步骤最好放在发布阶段,由脚手架操作)。通过mutation 侦听 .j_collector_container 容器的DOM子孙节点变化。如在observe事件处理函数中,计算 .j_collector_container 高度,如果大于屏幕高度则意味着首屏的DOM结构已渲染完毕,开始计算首屏时间。

在计算 .j_collector_container 高度时,最好采用限流策略,防止短时间内计算多次容器的布局信息,这也是无可奈何之举。

此处的伪代码如下:

// 记录首屏DOM元素的位置信息
var firstScreenDomReady = false;
var callback = function(records){
    if(firstScreenDomReady)
        return;

    // 此处需做throttle 处理
    for(var i=0,len=records.length;i<len;i++){
        // 判断首屏DOM渲染完毕的策略:
        // 判断collectWrapper元素高度是否大于首屏
        var cr = collectWrapper.getBoundingClientRect(),
        screenHeight = win.innerHeight;

        if(cr.top + cr.height >= screenHeight){
            firstScreenDomReady = true;
            recordFirstScreenLoad();
            return;
        }
    }
};
    
var mo = new MutationObserver(callback);

var option = {
    'childList': true,
    'subtree': true
};

var collectWrapper = document.querySelector('.j_collector_wrapper');
if(collectWrapper.getBoundingClientRect().height < win.innerHeight){
    mo.observe(collectWrapper, option);
}else{
    setTimeout(function(){
        recordFirstScreenLoad();
    });
}

总结

不管采用哪种方式,计算出来的首屏时间都不是准确的。而且在每种实现中都需要通过JS引擎与渲染引擎的bridge进行通信执行耗时的操作,如getBoundingClientRect和访问offsetTop属性导致relayout。不过这也是没有办法的办法,在浏览器不提供相关首屏API的前提下我们只有这么做。

另外,对比这三种实现(开发者手动打点、轮训、watch dog采集),针对一个复杂的电商首屏做了性能测试,该页面首屏部分有7个非常复杂的子组件,得到如下结果:

结果也符合我们的预期。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于cocos2dx客户端程序的自动更新解决方案

    转载请注明出处:帘卷西风的专栏(http://blog.csdn.net/ljxfblog)

    帘卷西风
  • 关于vue在首次加载时缓慢的解决办法,采用资源文件压缩的方式解决

    简介 第一次打包vue的项目部署到服务器下时,发现初次加载特别的缓慢,将近20s页面才加载出来,完全没有开发环境上的那么流畅。主要原因是页面在打包后如果不进行相...

    子润先生
  • 持续集成 — 构建接口自动化测试的一种解决方案

    给大家提供一种解决方案的思路,目前大部分同学接触到的jenkins构建自动化应该都是代码和jenkins服务在同一台服务器上面,但是很多时候事与愿违,也许公司的...

    DevOps时代
  • 关于eclipse启动时报Failed to create the Java Virtural Machine.错误的解决方案

    最近媳妇在学习android的开发,我给她把环境搭好以后。在网上下载了一个最新的eclpse,在启动的时候报错:Failed to create the Jav...

    业余草
  • 基于机器学习的启动耗时自动化测试方案

    当一个应用的用户越来越多,业务越来越复杂,性能问题就会突显,特别是在低端机上的用户感受尤为明显,甚至会影响到应用的用户活跃度、停留时长等重要指标,提升应用在中低...

    岛哥的质量效能笔记
  • 关于 Tomcat 启动时,解决控制台输出日志乱码问题的方案

    虽然即使不更改,也不影响使用,但是作为一个追求完美的处女座的人来说,实在有点儿受不了,在一上午的寻找之下,还是找到了解决方案。大家可以参考一下!

    Mokwing
  • 手淘店铺全链路性能优化

    店铺是导购中重要的一环,承接来自商品详情页、主分会场、主搜等数十亿的流量,店铺的性能体验就显得尤为重要。店铺作为流量大,架构复杂,形态多样,稳定性要求高的典型场...

    winty
  • IBC+Palette 实现屏幕内容编码优化

    https://www2.tutormeetplus.com/v2/render/playback?mode=playback&token=e632113e78...

    LiveVideoStack
  • 首发 | Wannacry勒索软件母体主程序逆向分析(含临时解决方案自动化工具)

    Wannacry勒索软件的背景大家都知道了,就是老美的重武器泄漏出来,结果被其他人捡了便宜所利用。类似这样的事情几年前我在以前的公司也做过,从国外弄来的高级样本...

    FB客服
  • 翻译 | 关键CSS和Webpack: 减少阻塞渲染的CSS的自动化解决方案

    原文地址: Critical CSS and Webpack: Automatically Minimize Render-Blocking CSS 原文作者:...

    iKcamp
  • 你看到的京东首页是如何出炉的?

    用户1756920
  • 技术揭秘 | 服务于130+客户的直播SDK是怎样炼成的?

    “ 导语:腾讯音视频实验室直播SDK基于QQ音视频多年在音视频技术领域的积累而搭建,广泛应用于公司内外部产品中,如坐拥大量草根网红的快手,电商类头部应用蘑菇街...

    腾讯多媒体实验室
  • 如何让视频会议在小程序上开起来

    |导语  使用企业微信跨组织间会议门槛较高,要求外部客户或合作伙伴先建立在企业微信的线上组织才可入会,通过引入小程序入会能力,降低跨组织会议的门槛; 为解决微...

    腾讯大讲堂
  • 前端工程实践之数据埋点分析系统(一)

    本文首发于政采云前端团队博客:前端工程实践之数据埋点分析系统(一) https://www.zoo.team/article/data-analysis-one

    政采云前端团队
  • 腾讯WeTest的小程序兼容测试实践之路

    ? 作 者 朱永俊,腾讯IEG高级工程师 商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。 作者导读 为了提升对微信小程序的测试能力, 腾讯W...

    WeTest质量开放平台团队
  • 前端黑科技:美团网页首帧优化实践

    本文根据美团资深研发工程师寒阳在美团技术沙龙第40期《前端遇上黑科技,打造全新界面体验与效率》的演讲内容整理而成。本文介绍了如何使用构建时预渲染技术,对移动端首...

    前端劝退师
  • 如何搭建一个高可用的服务端渲染工程

    可能大家在看到这个标题的时候,会觉得,只不过又是一篇烂大街的SSR从零入门的教程而已。别急,往下看,相信你或多或少会有一些不一样的收获呢。

    有赞coder
  • iOS支付项目实践专栏- 总目录(持续更新)

    本专栏以实战为线索,逐步深入iOS开发各个环节,掌握支付APP常用的基础功能(均含demo源码),打造完整native客户端工作流,提升工程化编码能力和思维能力...

    公众号iOS逆向
  • 采集、编码、组播 、推流和流媒体RTSP服务于一身的EasyScreenLive同屏功能组件为什么出现不能正常启动的情况?

    互联网同屏直播越来越火热,我们知道很多游戏主播都采用同屏的方式给观众直播,同屏直播组件层出不穷,EasyScreenLive就是TSINGSEE青犀视频团队研发...

    EasyNVR

扫码关注云+社区

领取腾讯云代金券