前端性能监控:window.performance

2018-03-08 09:44:15

window.performance 是W3C性能小组引入的新的API,目前IE9以上的浏览器都支持。一个performance对象的完整结构如下图所示:

memory字段代表JavaScript对内存的占用。

navigation字段统计的是一些网页导航相关的数据:

  1. redirectCount:重定向的数量(只读),但是这个接口有同源策略限制,即仅能检测同源的重定向;

  2. type 返回值应该是0,1,2 中的一个。分别对应三个枚举值:

    • 0 : TYPE_NAVIGATE (用户通过常规导航方式访问页面,比如点一个链接,或者一般的get方式)

    • 1 : TYPE_RELOAD (用户通过刷新,包括JS调用刷新接口等方式访问页面)

    • 2 : TYPE_BACK_FORWARD (用户通过后退按钮访问本页面)

最重要的是timing字段的统计数据,它包含了网络、解析等一系列的时间数据。

2.2.1 timing API

timing的整体结构如下图所示:

各字段的含义如下:

  • startTime:有些浏览器实现为navigationStart,代表浏览器开始unload前一个页面文档的开始时间节点。比如我们当前正在浏览baidu.com,在地址栏输入google.com并回车,浏览器的执行动作依次为:unload当前文档(即baidu.com)->请求下一文档(即google.com)。navigationStart的值便是触发unload当前文档的时间节点。

    如果当前文档为空,则navigationStart的值等于fetchStart。

  • redirectStartredirectEnd:如果页面是由redirect而来,则redirectStart和redirectEnd分别代表redirect开始和结束的时间节点;

  • unloadEventStartunloadEventEnd:如果前一个文档和请求的文档是同一个域的,则unloadEventStartunloadEventEnd分别代表浏览器unload前一个文档的开始和结束时间节点。否则两者都等于0;

  • fetchStart是指在浏览器发起任何请求之前的时间值。在fetchStart和domainLookupStart之间,浏览器会检查当前文档的缓存;

  • domainLookupStartdomainLookupEnd分别代表DNS查询的开始和结束时间节点。如果浏览器没有进行DNS查询(比如使用了cache),则两者的值都等于fetchStart

  • connectStartconnectEnd分别代表TCP建立连接和连接成功的时间节点。如果浏览器没有进行TCP连接(比如使用持久化连接webscoket),则两者都等于domainLookupEnd

  • secureConnectionStart:可选。如果页面使用HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0;

  • requestStart代表浏览器发起请求的时间节点,请求的方式可以是请求服务器、缓存、本地资源等;

  • responseStartresponseEnd分别代表浏览器收到从服务器端(或缓存、本地资源)响应回的第一个字节和最后一个字节数据的时刻;

  • domLoading代表浏览器开始解析html文档的时间节点。我们知道IE浏览器下的document有readyState属性,domLoading的值就等于readyState改变为loading的时间节点;

  • domInteractive代表浏览器解析html文档的状态为interactive时的时间节点。domInteractive并非DOMReady,它早于DOMReady触发,代表html文档解析完毕(即dom tree创建完成)但是内嵌资源(比如外链css、js等)还未加载的时间点;

  • domContentLoadedEventStart:代表DOMContentLoaded事件触发的时间节点:

    页面文档完全加载并解析完毕之后,会触发DOMContentLoaded事件,HTML文档不会等待样式文件,图片文件,子框架页面的加载(load事件可以用来检测HTML页面是否完全加载完毕(fully-loaded))。

  • domContentLoadedEventEnd:代表DOMContentLoaded事件完成的时间节点,此刻用户可以对页面进行操作,也就是jQuery中的domready时间;

  • domComplete:html文档完全解析完毕的时间节点;

  • loadEventStartloadEventEnd分别代表onload事件触发和结束的时间节点

2.2.2 计算性能指标

可以使用Navigation.timing 统计到的时间数据来计算一些页面性能指标,比如DNS查询耗时、白屏时间、domready等等。如下:

  • DNS查询耗时 = domainLookupEnd - domainLookupStart

  • TCP链接耗时 = connectEnd - connectStart

  • request请求耗时 = responseEnd - responseStart

  • 解析dom树耗时 = domComplete - domInteractive

  • 白屏时间 = domloadng - fetchStart

  • domready时间 = domContentLoadedEventEnd - fetchStart

  • onload时间 = loadEventEnd - fetchStart

2.2.3 Resource timing API

Resource timing API是用来统计静态资源相关的时间信息,详细的内容请参考W3C Resource timing。这里我们只介绍performance.getEntries方法,它可以获取页面中每个静态资源的请求,【以百度移动版首页的logo为例】如下:

 


可以看到performance.getEntries返回一个数组,数组的每个元素代表对应的静态资源的信息,比如上图展示的第一个元素对应的资源类型initiatorType是图片img,请求花费的时间就是duration的值。

关于Resource timing API的使用场景,感兴趣的同学可以深入研究。

 

复制代码

;
(function() {

    handleAddListener('load', getTiming)    function handleAddListener(type, fn) {        if(window.addEventListener) {
            window.addEventListener(type, fn)
        } else {
            window.attachEvent('on' + type, fn)
        }
    }    function getTiming() {        try {            var time = performance.timing;            var timingObj = {};            var loadTime = (time.loadEventEnd - time.loadEventStart) / 1000;            if(loadTime < 0) {
                setTimeout(function() {
                    getTiming();
                }, 200);                return;
            }

            timingObj['重定向时间'] = (time.redirectEnd - time.redirectStart) / 1000;
            timingObj['DNS解析时间'] = (time.domainLookupEnd - time.domainLookupStart) / 1000;
            timingObj['TCP完成握手时间'] = (time.connectEnd - time.connectStart) / 1000;
            timingObj['HTTP请求响应完成时间'] = (time.responseEnd - time.requestStart) / 1000;
            timingObj['DOM开始加载前所花费时间'] = (time.responseEnd - time.navigationStart) / 1000;
            timingObj['DOM加载完成时间'] = (time.domComplete - time.domLoading) / 1000;
            timingObj['DOM结构解析完成时间'] = (time.domInteractive - time.domLoading) / 1000;
            timingObj['脚本加载时间'] = (time.domContentLoadedEventEnd - time.domContentLoadedEventStart) / 1000;
            timingObj['onload事件时间'] = (time.loadEventEnd - time.loadEventStart) / 1000;
            timingObj['页面完全加载时间'] = (timingObj['重定向时间'] + timingObj['DNS解析时间'] + timingObj['TCP完成握手时间'] + timingObj['HTTP请求响应完成时间'] + timingObj['DOM结构解析完成时间'] + timingObj['DOM加载完成时间']);            for(item in timingObj) {
                console.log(item + ":" + timingObj[item] + '毫秒(ms)');
            }

            console.log(performance.timing);

        } catch(e) {
            console.log(timingObj)
            console.log(performance.timing);
        }
    }
})();

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
 
    <head>
        <meta charset="utf-8" />
        <title></title>
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <link rel="stylesheet" type="text/css" href="//cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" />
        <script src=""></script>
        <script type="text/javascript" src=""></script>
    </head>
 
    <body>
 
    </body>
 
</html>

  

 
 

  • 2019-08-22 13:35:27

    Generator函数的语法

    执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了是状态机还是一个遍历器对象生成函数。 返回遍历器对象,可以依次遍历Generator函数内部的每一个状态。

  • 2019-08-22 16:38:15

    理解JS原型对象与原型链(重要清晰)

    JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象对应拥有一个原型,对象以其原型为模板、从原型继承方法和属性。而同时原型也是对象,它也拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

  • 2019-08-22 17:26:21

    详解javaScript的深拷贝

    最开始意识到深拷贝的重要性是在我使用redux的时候(react + redux), redux的机制要求在reducer中必须返回一个新的对象,而不能对原来的对象做改动,事实上,当时我当然不会主动犯这个错误,但很多时候,一不小心可能就会修改了原来的对象,例如:var newObj = obj; newObj.xxx = xxx 实际上,这个时候newObj和obj两个引用指向的是同一个对象,我修改了newObj,实际上也就等同于修改了obj,这,就是我和深浅拷贝的第一次相遇。

  • 2019-08-22 19:14:21

    Android Studio 3.5最新特性

    Android Studio(以下简称为AS) 3.5正式版终于发布了,从第一个bate版本发布到正式版本,历时三个半月。AS一直以来被开发者吐槽,因此谷歌也放慢了版本的变化,对测试版本进行大力度的优化,提高了稳定性。从3.3版本开始,谷歌启动了名为Project Marble的计划,意为谷歌团队致力于使集成开发环境(IDE)的基本功能和流程变得坚如磐石,同时精炼和完善面向用户的功能。而AS 3.5则是Project Marble主要成果的版本,下面来介绍主要成果。

  • 2019-08-27 05:43:13

    Laravel 门面自动补全工具 laravel-ide-helper

    当我们在 PhpStorm 编辑器中,开发 Laravel 框架的项目时,很多类方法都不能自动补全和定位,比如 Facade 门面的方法,DB::table()、Route::get() 等。

  • 2019-08-28 08:28:36

    Js apply,call方法详解,及其apply()方法的妙用

    在给对象参数的情况下,如果参数的形式是数组的时候,比如apply示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就可以采用 apply , 如果我的Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));