前端质量监控之页面性能相关

2018-11-08 admin

前言

最近有幸参与一个前端质量监控类项目的重构,算是个人初次接触到前端质量监控相关的知识,对于其中的性能统计部分很感兴趣,查询资料之后写了文章,作为个人学习记录,如有错误,敬请斧正

1.页面整体性能

通过浏览器提供的 window.performance.timing 方法,我们能够得到网页每个处理阶段的精确时间。打开一个页面后,这些信息会被浏览器记录下来,我们直接在控制台输出,就可以查看结果

clipboard.png

该对象下有多个字段,每个字段都对应着浏览器加载一个页面的某个阶段,下面这张图详细的展示了浏览器加载一个页面的详细过程

https://segmentfault.com/img/… clipboard.png

    window.performance = {
        timing: {
            // 同一个域下,前一个网 unload 的时间戳,
            // 如果直接复制地址进入或者不是同域
            // 则与 fetchStart 值相等
            navigationStart: 1441112691935,

            // 前一个网页unload 的时间戳,
            // 如果无前一个网页 unload
            // 或者前一个网页与当前页面不同域,则值为 0
            unloadEventStart: 0,

            // 和 unloadEventStart 相对应
            // 返回前一个网页 unload  事件绑定的回调函数执行完毕的时间戳
            unloadEventEnd: 0,

            // 第一个 HTTP 重定向发生时的时间。
            // 有跳转且是同域名内的重定向才算
            // 否则值为 0 
            redirectStart: 0,

            // 最后一个 HTTP 重定向完成时的时间
            // 有跳转且是同域名内部的重定向才算
            // 否则值为 0 
            redirectEnd: 0,

            // 浏览器准备好使用 HTTP 请求抓取文档的时间
            // 发生在检查本地缓存之前
            fetchStart: 1441112692155,

            // DNS 域名查询开始的时间
            // 如果使用了本地缓存(即无 DNS 查询)或持久连接
            // 则与 fetchStart 值相等
            domainLookupStart: 1441112692155,

            // DNS 域名查询完成的时间
            // 如果使用了本地缓存(即无 DNS 查询)或持久连接
            // 则与 fetchStart 值相等
            domainLookupEnd: 1441112692155,

            // HTTP(TCP) 开始建立连接的时间
            // 如果是持久连接,则与 fetchStart 值相等
            // 注意如果在传输层发生了错误且重新建立连接,
            // 则显示的是新建立的连接开始的时间
            connectStart: 1441112692155,

            // HTTP(TCP) 完成建立连接的时间(完成握手)
            // 如果是持久连接,则与 fetchStart 值相等
            // 注意如果在传输层发生了错误且重新建立连接,
            // 则显示的是新建立的连接完成的时间
            // 注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过
            connectEnd: 1441112692155,

            // HTTPS 连接开始的时间,
            // 如果不是安全连接,则值为 0
            secureConnectionStart: 0,

            // HTTP 请求读取真实文档开始的时间(完成建立连接
            // 包括从本地读取缓存
            // 连接错误重连时,这里显示的也是新建立连接的时间
            requestStart: 1441112692158,

            // HTTP 开始接收响应的时间(获取到第一个字节)
            // 包括从本地读取缓存
            responseStart: 1441112692686,

            // HTTP 响应全部接收完成的时间(获取到最后一个字节)
            // 包括从本地读取缓存
            responseEnd: 1441112692687,

            // 开始解析渲染 DOM 树的时间
            // 此时 Document.readyState 变为 loading
            // 并将抛出 readystatechange 相关事件
            domLoading: 1441112692690,

            // 完成解析 DOM 树的时间
            // Document.readyState 变为 interactive
            // 并将抛出 readystatechange 相关事件
            // 注意只是 DOM 树解析完成
            // 此时并没有开始加载网页内的资源
            domInteractive: 1441112693093,

            // DOM 解析完成后,网页内资源加载开始的时间
            // 在 DOMContentLoaded 事件抛出前发生
            domContentLoadedEventStart: 1441112693093,

            // DOM 解析完成后,
            // 网页内资源加载完成的时间
            // 如 JS 脚本加载执行完
            domContentLoadedEventEnd: 1441112693101,

            // DOM 树解析完成
            // 且资源也准备就绪的时间
            // Document.readyState 变为 complete
            // 此时抛出 readystatechange 相关事件
            domComplete: 1441112693214,

            // load 事件发送给文档
            // 也即 load 回调函数开始执行的时间
            // 注意如果没有绑定 load 事件,值为 0
            loadEventStart: 1441112693214,

            // load 事件的回调函数执行完毕的时间
            loadEventEnd: 1441112693215
        }
    }

通过这些值,我们就能得到某个阶段具体的时间差,进行一些简单的计算, 就能够得到网页的各项性能数据,便于我们在某个阶段做优化


    // DOM解析时间
    var domParseTime = domComplete - responseEnd;

    // DNS解析时间
    var domainLookUpTime = domainLookupEnd - domainLookupStart;

2. 页面中各个资源的性能

上面我们用 window.performance.timing 方法得到的是整个页面的耗时,在一些情况下,我们想要知道页面上某个静态资源的加载时间,那么我们就可以用三个方法来获取这些信息

  • window.performance.getEntries // 返回网页中所有资源和标记的数据
  • window.performance.getEntriesByType // 根据entryType返回数据
  • window.performance.getEntriesByName // 根据name返回数据

其中 window.performance.getEntries 可以获取到所有资源信息和所有标记信息(何为标记信息,我们后面会讲到), 而我们想看的只是 entryType: "resource" 这些资源的信息,所以我们可以直接使用 window.performance.getEntriesByType('resource') 方法来获得我们的信息,该方法会返回一个数组,包含字段如下

    var entries = [{  
        // 资源的绝对路径
        name: "http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js",
        // 资源类型
        entryType: "resource",
        // css: 通过css请求的资源。类型不定
        // img: 图片
        // link: 通过link标签请求的资源: css/favicon
        // script: js文件
        // xmlhttprequest: 一般为GET方式的数据接口
        initiatorType: "script", 
        // 加载时间
        duration: 18.13399999809917,

        // 这些字段的意义同上面
        // 不同之处只表示这一个资源的时间
        redirectStart: 0,
        redirectEnd: 0,
        fetchStart: 233.346999992829828,
        domainLookupStart: 0,
        domainLookupEnd: 0,
        connectStart: 0,
        connectEnd: 0,
        secureConnectionStart: 0,
        requestStart: 0,
        responseStart: 0,
        responseEnd: 442.7109999960521,
        startTime: 424.57699999795295
  }];

这些数据里面,initiatorType的值需要注意下, initiatorType 并不是指资源的类型,而是指 请求该资源的请求类型, 如上图所示 initiatorType: 'css' 的时候,资源文件并不是一个css文件,而是一张 img图片,表明该图片是通过css去请求到的(通常是设置背景图),而直接在网页中通过 <img /> 或在代码中通过 new Image 请求的图片,initiatorType 值都为 img,我们以下面这段代码为例

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <link rel="stylesheet" href="index.css">
            <link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css">
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
            <style>
                body > div {
                    height: 300px;
                    width: 300px;
                    margin: 10px;
                    float: left;
                }

                .css-img div {
                    background: url('http://temp.im/110x110/FF2D55/000') no-repeat;
                }
            </style>
        </head>
        <body>
            <div class="css-img">
                <p>通过CSS加载的图片</p>
                <div></div>
            </div>

            <div class="normal-img">
                <p>通过 img 标签直接加载</p>
                <img src="http://temp.im/178x178/007AFF/fff" />
            </div>

            <div class="new-img">
                <p>通过 new Image() 加载的图片</p>

            </div>
        </body>
        <script>

            window.onload = function () {
                var tempImage = new Image();
                tempImage.src = "http://temp.im/288x288/FF9500/000";
                document.querySelector('.new-img').appendChild(tempImage)
            }

      </script>
    </html>

clipboard.png

打开页面之后在控制台输出结果, 也验证了结果

clipboard.png

另外值得注意的一个点是,除了这三张图片,我们在页面中还加载了2个css文件和1个js文件,分别是

  • index.css // 本地文件
  • bootstrap.min.css // 远程
  • jquery.min.js // 远程

仔细观察的话,会发现这几个文件有一些不同之处,我们对数据稍作处理,方便参看

我们会发现,其中的 bootstrap.min.css 的很多字段数据都为0,查询资料之后才知道,该方法通常情况下只能获得本域名下资源的加载信息(所以 index.css 的信息直接就能获取到)

如果想要获取远程资源的加载信息的只有在response里面显式的设置 Timing-Allow-Origin:* 类似于(Acces-Control-Allow-Origin的设置)

我们直接打开了 jquery.min.js 连接, 看到了 Timing-Allow-Origin 字段,所以我们能够获得该资源的准确信息, 而在 bootstrap.min.css 的返回中,我们没有找到 Timing-Allow-Origin 字段, 所以数据都显示为0

clipboard.png


3. 代码执行计时

通常情况下,我们想要取得一段代码的执行时间,会做以下操作

      var start = +new Date();
      for (var i = 0; i < 100; i++) {
          ret.push(i);
      }
      var end = +new Date();
      console.log('执行时间', end - start);

但不足之处在于, new Date 的精确度只到秒, 而有时候我们的代码执行在1s之内,计算出的差值为0,此时就显得无能为力。所以,我们可能通过如下两种方式,解决这个问题

1. 通过 window.performance.now

    var start = window.performance.now();
    for (var i = 0; i < 100; i++) {
        ret.push(i);
    }
    var end = window.performance.now();
    console.log('执行时间', end - start);

使用方式十分简单,该方法能够计算出精确到 百万分之一秒 的时间差。与 new Date 不同之处在于 window.performance.now 方法返回的是相对于 performance.timing.navigationStart 即页面初始化的时间,但我们的关注点是差值, 所以时间从哪儿算起,对于我们来说并不重要

2. 使用 window.performance.markwindow.performance.measure

    // 标记开始
    window.performance.mark("start");
    for (var i = 0; i < 100; i++) {
        ret.push(i);
    }
    // 标记结束
    window.performance.mark("end");
    // 计算差值并命名为difference, 无返回
    window.performance.measure("difference", "start", "end");

通过 window.performance.mark 方法,我们在指定地方打上一个记号,此时不会返回任何值,会被记录到performance 对象里, entryType 被默认标记为了 mark

然后通过 window.performance.measure 计算出差值,并通过第一个参数对其赋名为 difference, 此时也没有返回值,这些值也被记录到了 performance 对象里,它们的 entryType 被默认标记为了 measure.

clipboard.png

所以我们可以通过两种方式来查看差值

  • window.performance.getEntriesByType('measure')
  • `window.perform

clipboard.png esByName(‘difference’)`

![图片上传中…]

在使用完了之后,我们还可以对这些标识,进行统一的清理

    // 清除指定标记点
    window.performance.clearMarks('start');  
    // 清除所有标记点
    window.performance.clearMarks();

    // 清除指定差值数据
    window.performance.clearMeasures('difference');  
    // 清除所有差值数据
    window.performance.clearMeasures();  

通过这两种方式,都可以精确的计算出十分精确的时间差值,那如何做选择呢? 我的建议是,如果只是单纯的想要得到少数的差值,直接在代码中使用 now, 而如果需要统计大量的值, 就应该使用 markmeasure 方法,因为这些值都会存在 performance 对象里,我们可以在任意需要的时候,通过读取数组,十分便捷的对数据做统一处理与管理

API支持 && 参考

原文链接:https://segmentfault.com/a/1190000016949703

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-44902.html

文章标题:前端质量监控之页面性能相关

相关文章
前端MV*框架的意义
经常有人质疑,在前端搞MV有什么意义?也有人提出这样的疑问:以AngularJS,Knockout,BackBone为代表的MV框架,它跟jQuery这样的框架有什么区别?我jQuery用得好好的,有什么必要再引入这种框架? 回答这些问题之...
2016-03-11
jQuery中DOM树操作之复制元素的方法
本文实例讲述了jQuery中DOM树操作之复制元素的方法。分享给大家供大家参考。具体分析如下: 复制元素 前面提到的操作包括:插人新创建的元素、将元素从文档中的一个位置移动 到另一个位置,以及通过新元素来包装已有的元素。可是,有时候也会用到...
2015-11-13
Node.js的不足之处
跨平台编程能力不够强大 Bowery团队指出Go能很方便地在不同系统里进行程序编译,这是他们转入Go的重要原因之一。 作为开发平台,对Linux,Windows,OSX等常见操作系统提供支援是能否吸引开发者的基本要素。在Go中,开发者可以针...
2015-11-12
前端问答社区成立了
由雷锋网友提供的给大家相互交流的前端问答社区正式上线了,欢迎大家来此相互交流相互学习 ...
2016-03-30
前端工程师应该具备的三种思维
如果你是一个天才等级的工程师(马上可以离开),可以独立完成一个很多事情,你可以是一个怪咖,因为我相信没有一个人不会不佩服你。但现实归现实,多数人都不是天才,而我们在职场上也不是单打独斗,我们需要团队合作,需要协调和配合,需要考虑除了代码以外...
2016-01-13
JavaScript正则进阶之路——活学妙用奇淫正则表达式
有些童鞋肯定有所疑惑,花了大量时间学习正则表达式,却发现没有用武之地,正则不就是验证个邮箱嘛,其他地方基本用不上,其实,大部分人都是这种感觉,所以有些人干脆不学,觉得又难又没多大用处。殊不知,想要成为编程大牛,正则表达式必须玩转,GitH...
2017-05-31
ajax教程之ajax使用Http请求
ajax中是如何让使用http请求的呢? 在传统的JS编程中,如果您希望从服务器上的文件或数据库中得到任何的信息,或者向服务器发送信息的话,就必须利用一个 HTML 表单向服务器 GET 或 POST 数据。而用户则需要单击“提交”按钮来发...
2015-11-12
性能与生态双突破 HTML5重现爆发曙光
走过早熟的WebAPP,也经历过概念性的“轻应用”,HTML5这个被视作移动互联网未来的技术标准,终于在2015年看到了爆发的曙光。1月 15日,搜狐发布基于HTML5的“手机搜狐网3.0,加上微信几天前开放HTML5接口,HTML5很可能...
2015-11-12
梳理前端开发使用eslint-prettier检查和格式化代码
问题痛点 在团队的项目开发过程中,代码维护所占的时间比重往往大于新功能的开发。因此编写符合团队编码规范的代码是至关重要的,这样做不仅可以很大程度地避免基本语法错误,也保证了代码的可读性。 对于代码版本管理系统(svn 和 git或者其他)...
2018-05-07
DOM之通俗易懂讲解
DOM 是所有前端开发每天打交道的东西,但是随着 jQuery 等库的出现,大大简化了 DOM 操作,导致大家慢慢的 “遗忘” 了它的本来面貌。不过,要想深入学习前端知识,对 DOM 的了解是不可或缺的,所以本文力图系统的讲解下 DOM 的...
2016-01-13
回到顶部