如何计算首屏加载时间?

作移动web页面,受移动网络网速和终端性能影响,咱们常常要关注首屏内容展现时间(如下简称首屏时间)这个指标,它衡量着咱们的页面是否能在用户耐心消磨完以前展现出来,很大程度影响着用户的使用满意度。html

首屏时间的定义web

工信部在《宽带速率的测试方法用户上网体验》规范标准中对首屏时间的定义为:canvas

浏览器显示第一屏页面所消耗的时间,以800x600像素尺寸为标准,从开始加载到浏览器页面显示高度达到600像素且此区域有内容显示的时间。api

也就是说用户可以看到区域内全部元素加载完的时间。浏览器

一个页面的总加载时间要比首屏时间长,但对于最终用户体验而言,当内容充满首屏的区域时,用户就能够看到网站的主要内容并能够进行各自的选择了。首屏时间的快与慢,直接影响到了用户对网站的认知度。网络

在国内的网络条件下,一般一个网站,若是首屏时间2秒之内是比较优秀的,5秒之内用户能够接受,10秒以上就不可容忍了。dom

对于页面的加载时间,dom都提供了api接口,好比,整个页面dom树的构建时间,咱们经过打点DOMContentLoaded,就能获得。整个页面加载完成,包括图片,视频等外部资源,咱们经过window.onload的方法也能够获得。可是对于首屏,没有这样的接口提供。异步

那么如何去计算首屏的加载时间?性能

首先要解决的问题是哪些属于首屏的内容。因为手机屏幕尺寸的多样性,同一页面在手机屏幕上用户所能看到的首屏内容有可能不同。因此须要去判断哪些元素属于首屏元素而且该元素加载是否完成。对于非可替换元素,dom的加载完成说明了该元素已经完成加载,而对于一些可替换元素,如img标签,须要外部资源的加载完成才能有实际内容的展现,而页面耗时最大的部分也是这些外部资源的加载。测试

由于一般须要考虑首屏时间的页面,都是由于在首屏位置内放入了较多的图片资源。现代浏览器处理图片资源时是异步的,会先将图片长宽应用于页面排版,而后随着收到图片数据由上至下绘制显示的。而且浏览器对每一个页面的TCP链接数限制,使得并非全部图片都能马上开始下载和显示。所以咱们在DOM树构建完成后便可遍历得到全部在设备屏幕高度内的全部图片资源标签,在全部图片标签中添加document.onload事件,在整页加载完成(window.onLoad事件发生)时遍历图片标签并得到以前注册的document.onload事件时间的最大值,该最大值减去navigationStart即认为近似的首屏时间。而对于页面没有图片的页面,咱们能够近似认为首屏的加载时间为dom完成的时间。

 

function firstScreen() {
    //收集全部页面的加载时间
    var imgs = document.getElementsByTagName("img");
    var fsItems = [];
    var loadEvent = function() {
        //gif避免
        if (this.removeEventListener) {
            this.removeEventListener("load", loadEvent, false);
        }
        var curTime = +new Date;
        fsItems.push({
            img: this,
            time: curTime
        });
    }
    for (var i = 0; i < imgs.length; i++) {
        (function() {
            var img = imgs[i];
            if (img.addEventListener) {
                !img.complete && img.addEventListener("load", loadEvent, false);
            } else if (img.attachEvent) {
                img.attachEvent("onreadystatechange", function() {
                    if (img.readyState == "complete") {
                        loadEvent.call(img, loadEvent);
                    }
                });
            }
        })();
    }

    //获取元素在dom中的位置
    function getOffsetTop(elem) {
        var top = 0;
        top = window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop;
        top += elem.getBoundingClientRect().top;
        return top;
    }

    function findMaxTime() {
        var sh = document.documentElement.clientHeight,
            maxTime = 0;
        for (var i = 0; i < fsItems.length; i++) {
            var item = fsItems[i],
                img = item['img'],
                time = item['time'],
                top = getOffsetTop(img);
            if (top > 0 && top < sh) { //找首屏中的图片
                maxTime = time > maxTime ? time : maxTime;
            }
        }
        return maxTime;
    }
    window.addEventListener('load', function() {
        var imgTime = findMaxTime(),
            domTime = window.performance.timing.domInteractive, //dom完成时间
            speedTime,
            startTime = window.performance.timing.navigationStart || window.performance.timing.startTime, //页面首页时间
            screenTime = imgTime > 0 ? imgTime : domTime; //若是没有图片,直接取dom时间
        speedTime = screenTime - startTime;
        console.log(speedTime);
    });

}

firstScreen();

这里须要注意一点,须要在页面整个onload完成以后再去计算首屏,由于可替换元素若是没有固定好高度有可能会致使在渲染过程当中页面重排。

以上就是经过首屏高度图片加载的办法实现的统计,这种方法可以解决对于首屏内容在服务端生成的状况,有时候咱们首屏数据须要经过异步请求得到,这种方式就不适合了。那么这类数据又有什么解决的办法。

首先,咱们的数据异步请求,页面的渲染依赖于数据接口,咱们能够在首屏的接口返回时间打点。这种作法可以统计出时间,可是对于业务逻辑的代码依赖性强,不可能抽象出普用性的api接口。

这里提出另一种方法:图像类似度比较法,经过比较连续截屏图像的像素点变化趋势肯定首屏时间,这里有个问题,经过连续比对的方法,咱们只能得出首屏加载完成的时间区间段,同时这种截图的方法会比较消耗本地设备的运行资源。代码方法实现以下:

 

function firstScreen() {
    var winWidth = document.documentElement.clientWidth,
        winHeight = document.documentElement.clientHeight,
        nineDots = findScreenDot(),
        preData = 0,
        funStartTime, timer, runTime;
    printScreen();

    //找首屏上的九个点
    function findScreenDot() {
        var sw = Math.ceil(winWidth / 6),
            sh = Math.ceil(winHeight / 6),
            dotArry = [];
        for (var i = 0; i < 3; ++i) {
            for (var j = 0; j < 3; ++j) {
                dotArry.push([sw * (i * 2 + 1), sh * (j * 2 + 1)])
            }
        }
        return dotArry;
    }

    funStarTime = +new Date();
    printScreen();

    //计算时间
    function calculateTime(time) {
        var startTime = window.performance.timing.navigationStart || window.performance.timing.startTime; //页面首页时间
        console.log(time - startTime - runTime);
    }
    //截图
    function printScreen() {
        funStartTime = +new Date();
        html2canvas(document.body).then(function(canvas) {
            var context = canvas.getContext('2d');
            var imageData = context.getImageData(0, 0, winWidth, winHeight);
            //截图初始化费时
            if (preData == 0) {
                runTime = +new Date() - funStartTime;
            }
            var colorTotal = 0;
            //对9个点的颜色红色通道像素值求值
            for (var i = 0, length = nineDots.length; i < length; ++i) {
                colorTotal += imageData.data[((nineDots[i][0] * (imageData.width * 4)) + (nineDots[i][1] * 4))]
            }
            //先后数据相同,能够上报时间
            if (preData == colorTotal && preData != 0) {
                calculateTime(+new Date())
                clearTimeout(timer);
            } else {
                preData = colorTotal;
                timer = setTimeout(function() {
                    printScreen();
                }, 100)
            }
        });
    }

}
firstScreen();

这里采用了html2canvas插件,每100ms截取屏幕的,而后获取屏幕九宫格每一格中心点的,获取红色通道的像素相加获得一个值,经过不断截屏和比较这个求和的值,监控出首屏是否加载完毕。

经过对以上两种方法的比对,截屏图像类似度比较的方法最为科学和直观,可是比较消耗本地设备的运行资源。并且因为比较复杂的运算,会影响到页面逻辑脚本执行的性能。因此在首屏测试的时候,推荐使用首屏高度内图片加载的方法,计算它们加载完的时间去获得首屏时间,这样比较符合网页的实际体验而且比较节省设备运行资源。

相关文章
相关标签/搜索