前端性能监控 前端设计 后端设计 1、报表展示 2、图表展示


白屏时间(first Paint Time)——用户从打开页面开始到页面开始有东西呈现为止

如何获取:

1、chrome 高版本:

window.chrome.loadTimes().firstPaintTime loadTimes获取的结果。

2、其他版本的浏览器

头部资源加载时间结束时间-首字节时间开始时间

var firstPaintTime = end_time - performance.timing.navigationStart
performance.timing.navigationStart首字节时间开始时间是由浏览器提供的函数
end_time需要我们计算
<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="UTF-8"/>
    <script>
      var start_time = +new Date; //测试时间起点,实际统计起点为 DNS 查询
    </script>
    <!-- 3s 后这个 js 才会返回 -->
    <script src="script.php"></script>  
    <script>
      var end_time = +new Date; //时间终点
      var headtime = end_time - start_time; //头部资源加载时间    
      console.log(headtime);
    </script>
    </head> 
    <body>     
    <p>在头部资源加载完之前页面将是白屏</p>
    <p>script.php 被模拟设置 3s 后返回,head 底部内嵌 JS 等待前面 js 返回后才执行</p>
    <p>script.php 替换成一个执行长时间循环的 js 效果也一样</p>  
    </body>
</html>

首屏时间——用户浏览器首屏内所有内容都呈现出来所花费的时间

首屏时间的统计比较复杂,因为涉及图片等多种元素及异步渲染等方式。观察加载视图可发现,影响首屏的主要因素的图片的加载。通过统计首屏内图片的加载时间便可以获取首屏渲染完成的时间。统计流程如下:

  首屏位置调用 API 开始统计 -> 绑定首屏内所有图片的 load 事件 -> 页面加载完后判断图片是否在首屏内,找出加载最慢的一张 -> 首屏时间

  这是同步加载情况下的简单统计逻辑,另外需要注意的几点:

  • 页面存在 iframe 的情况下也需要判断加载时间
  • gif 图片在 IE 上可能重复触发 load 事件需排除
  • 异步渲染的情况下应在异步获取数据插入之后再计算首屏
  • css 重要背景图片可以通过 JS 请求图片 url 来统计(浏览器不会重复加载)
  • 没有图片则以统计 JS 执行时间为首屏,即认为文字出现时间
 
//IE gif重复onload解决
var img=new Image(); 
img.load=function(){ 
//do something 
img.load=null;//重新赋值为null 
} 
img.src='××.gif';

   

  统计方法1:

  原理:在首屏渲染之前埋上处理逻辑,使用定时器不断的去检测img节点的图片。判断图片是否在首屏和加载完成,找到首屏中加载时间最慢的的图片完成的时间,从而计算出首屏时间。如果首屏有没有图片,如果没图片就用domready时间。

  缺点: 1.浏览器定时器最大精度为55ms 2.背景图片加载没有计算在内 3.不断检测并执行的脚本耗时

/1,获取首屏基线高度
//2,计算出基线dom元素之上的所有图片元素
//3,所有图片onload之后为首屏显示时间
  function getOffsetTop(ele) {
    var offsetTop = ele.offsetTop;
    if (ele.offsetParent !== null) {
      offsetTop += getOffsetTop(ele.offsetParent);
    }
    return offsetTop;
  }
  var firstScreenHeight = win.screen.height;
  var firstScreenImgs = [];
  var isFindLastImg = false;
  var allImgLoaded = false;
  var t = setInterval(function() {
    var i, img;
    if (isFindLastImg) {
      if (firstScreenImgs.length) {
        for (i = 0; i < firstScreenImgs.length; i++) {
          img = firstScreenImgs[i];
          if (!img.complete) {
            allImgLoaded = false;
            break;
          } else {
            allImgLoaded = true;
          }
        }
      } else {
        allImgLoaded = true;
      }
      if (allImgLoaded) {
        collect.add({
          firstScreenLoaded: startTime - Date.now()
        });
        clearInterval(t);
      }
    } else {
      var imgs = body.querySelector('img');
      for (i = 0; i < imgs.length; i++) {
        img = imgs[i];
        var imgOffsetTop = getOffsetTop(img);
        if (imgOffsetTop > firstScreenHeight) {
          isFindLastImg = true;
          break;
        } else if (imgOffsetTop <= firstScreenHeight 
           && !img.hasPushed) {
          img.hasPushed = 1;
          firstScreenImgs.push(img);
        }
      }
    }
  }, 0);

  doc.addEventListener('DOMContentLoaded', function() {
    var imgs = body.querySelector('img');
    if (!imgs.length) {
      isFindLastImg = true;
    }
  });

  win.addEventListener('load', function() {
    allImgLoaded = true;
    isFindLastImg = true;
    if (t) {
      clearInterval(t);
    }
    collect.log(collect.global);
  });   

  统计方法2:

  原理:对于网页高度小于屏幕的网站来说,只要在页面底部加上脚本打印当前时间即可;或者对于网页高度大于一屏的网页来说,只要在估算接近于一屏幕的元素的位置后,打印一下当前时间。当然这个时间要得把首屏中所有图片的加载时间也算上。

  缺点: 1.需要每个页面手动加入到对应位置 2.背景图片加载没有计算在内

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
        <script type="text/javascript">
            window.logInfo = {};
            window.logInfo.openTime = performance.timing.navigationStart;
        </script>
    </head>
    <body>
        <div>这是第一屏,这是第一屏</div>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <div>第一屏结尾,第一屏结尾</div>
        <script type="text/javascript">
            (function logFirstScreen() {
                var images = document.getElementsByTagName('img');
                var iLen = images.length;
                var curMax = 0;
                var inScreenLen = 0;
                // 图片的加载回调
                function imageBack() {
                    this.removeEventListener
                    && this.removeEventListener('load', imageBack, !1);
                    if (++curMax === inScreenLen) {
                        // 如果所有在首屏的图片均已加载完成了的话,发送日志
                        log();
                    }   
                } 
                // 对于所有的位于指定区域的图片,绑定回调事件
                for (var s = 0; s < iLen; s++) {
                    var img = images[s];
                    var offset = {
                        top: 0
                    };
                    var curImg = img;
                    while (curImg.offsetParent) {
                        offset.top += curImg.offsetTop;
                        curImg = curImg.offsetParent;
                    }
                    // 判断图片在不在首屏
                    if (document.documentElement.clientHeight < offset.top) {
                        continue;
                    }
                    // 图片还没有加载完成的话
                    if (!img.complete) {
                        inScreenLen++;
                        img.addEventListener('load', imageBack, !1);
                    }
                }
                // 如果首屏没有图片的话,直接发送日志
                if (inScreenLen === 0) {
                    log();
                }
                // 发送日志进行统计
                function log () {
                    window.logInfo.firstScreen = +new Date() - window.logInfo.openTime;
                    console.log('首屏时间:', window.logInfo.firstScreen + 'ms');
                }
            })();
        </script>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
    </body>
</html>

用户可操作时间(dom Interactive)——用户可以进行正常的点击、输入等操作,默认可以统计domready时间,因为通常会在这时候绑定事件操作

 用户可操作为所有DOM都解析完毕的时间,默认可以统计domready时间,因为通常会在这时候绑定事件操作。对于使用了模块化异步加载的 JS 可以在代码中去主动标记重要 JS 的加载时间,这也是产品指标的统计方式。

  使用jquery中的$(document).ready()即是此意义 window.performance.timing.domInteractive window.performance.timing.domContentLoadedEventStart

  计算公式:

performance.timing.domInteractive - performance.timing.navigationStart

总下载时间——页面所有资源都加载完成并呈现出来所花的时间,即页面 onload 的时间

 默认可以统计onload时间,这样可以统计同步加载的资源全部加载完的耗时。如果页面中存在很多异步渲染,可以将异步渲染全部完成的时间作为总下载时间。

  计算公式:

performance.timing.loadEventStart- performance.timing.navigationStart

后端设计


1、报表展示

将各项指标展示在PC端浏览器界面上。

2、图表展示

可以采用D3.js或者百度的Echart对分析结果进行图表展示。