前端性能监控之 window.performance 属性
时间:12-14来源:作者:点击数:
window.performance( www.w3 组织网/TR/2014/WD-navigation-timing-2-20140325/) 是W3C性能小组引入的新的API,目前IE9以上的浏览器都支持。一个performance对象的完整结构如下图所示:
Window.performance
虽然叫 Timing API 但是用起来却是 window.performance
// 兼容性写法
const performance = window.performance || window.msPerformance || window.webkitPerformance;
memory 字段代表 JavaScript 对内存的占用。
performance.memory 内存
- usedJSHeapSize:JS 对象(包括V8引擎内部对象)占用的内存
- totalJSHeapSize:可使用的内存
- jsHeapSizeLimit:内存大小限制
performance.navigation 我从哪里来
- redirectCount:如果有重定向的话,页面通过几次重定向跳转而来
- type:
- 0 即 TYPE_NAVIGATENEXT 正常进入的页面(非刷新、非重定向等)
- 1 即 TYPE_RELOAD 通过 window.location.reload() 刷新的页面
- 2 即 TYPE_BACK_FORWARD 通过浏览器的前进后退按钮进入的页面(历史记录)
- 255 即 TYPE_UNDEFINED 非以上方式进入的页面
navigation 字段统计的是一些网页导航相关的数据:
- redirectCount:重定向的数量(只读),但是这个接口有同源策略限制,即仅能检测同源的重定向;
- type 返回值应该是 0,1,2 中的一个。分别对应三个枚举值:
- 0 : TYPE_NAVIGATE (用户通过常规导航方式访问页面,比如点一个链接,或者一般的get方式)
- 1 : TYPE_RELOAD (用户通过刷新,包括JS调用刷新接口等方式访问页面)
- 2 : TYPE_BACK_FORWARD (用户通过后退按钮访问本页面)
最重要的是 timing 字段的统计数据,它包含了网络、解析等一系列的时间数据。
performance.timing 时间
2.2.1 timing API
timing 的整体结构如下所示:
- navigationStart:在同一个浏览器上下文中,前一个网页与当前页面不一定同域 unload 的时间戳,如果无前一个网页 unload 则与 fetchStart 值相等
- unloadEventStart:前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload或者前一个网页与当前页面不同域,则值为 0
- unloadEventEnd:和 unloadEventStart 相对应,返回前一个网页 unload 事件绑定的回调函数执行完毕的时间戳
- edirectStart:第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0
- edirectEnd:最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0
- fetchStart:浏览器准备好使用HTTP请求抓取文档的时间,这发生在检查本地缓存之前
- domainLookupStart:DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
- domainLookupEnd:DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
- connectStart:HTTP(TCP)开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等注意如果在传输层发生了错误自重新建立连接,则这里显示的是新建立的连接开始的时间
- connectEnd:HTTP(TCP)完成建立连接的时间(完成握手)如果是持久连接,则与 fetchStart 值相等注意如果在传输层发生了错误自重新建立连接,则这里显示的是新建立的连接完成的时间注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过
- secureConnectionStart:HTTPS 连接开始的时间,如果不是安全连接,则值为 0
- equestStart:HTTP 请求读取真实文档开始的时间(完成建立连接)包括从本地读取缀存连接错误重连时,这里显示的也是新建立连接的时
- esponseStart:HTTP 开始接收响应的时间(获取到第一个字节)包括从本地读取缓存
- esponseEnd:HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存
- domLoading:开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件
- domlnteractive:完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件注意只是DOM树解析完成,这时候并没有开始加载网页内的资源
- domContentLoadedEventStart:DOM 解析完成后,网页内资源加载开始的时间在 DOMContentLoaded 事件抛出前发生
- domContentLoadedEventEnd:DOM解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)
- domComplete:DOM树解析完成,.且资源也准备就绪的时间,Document..readyState 变为 complete,并将抛出 readystatechange 相关事件
- oadEventStart:Ioad 事件发送给文档,也即 Ioad 回调函数开始执行的时间注意如果没有
各字段的含义如下:
- startTime:有些浏览器实现为 navigationStart( developer.mozilla 组织网/en-US/docs/Web/API/PerformanceTiming/navigationStart),代表浏览器开始 unload 前一个页面文档的开始时间节点。比如我们当前正在浏览 baidu.com,在地址栏输入google.com并回车,浏览器的执行动作依次为:unload 当前文档(即 http://baidu.com)->请求下一文档(即 http://google.com)。navigationStart 的值便是触发unload当前文档的时间节点。
如果当前文档为空,则 navigationStart 的值等于 fetchStart。
- redirectStart 和 redirectEnd:如果页面是由redirect而来,则 redirectStart 和 redirectEnd 分别代表 redirect 开始和结束的时间节点;
- unloadEventStart 和 unloadEventEnd:如果前一个文档和请求的文档是同一个域的,则 unloadEventStart 和 unloadEventEnd 分别代表浏览器 unload 前一个文档的开始和结束时间节点。否则两者都等于0;
- fetchStart 是指在浏览器发起任何请求之前的时间值。在 fetchStart 和 domainLookupStart 之间,浏览器会检查当前文档的缓存;
- domainLookupStart 和 domainLookupEnd 分别代表DNS查询的开始和结束时间节点。如果浏览器没有进行DNS查询(比如使用了cache),则两者的值都等于fetchStart;
- connectStart 和 connectEnd 分别代表 TCP 建立连接和连接成功的时间节点。如果浏览器没有进行TCP连接(比如使用持久化连接webscoket),则两者都等于domainLookupEnd;
- secureConnectionStart:可选。如果页面使用 HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0;
- requestStart 代表浏览器发起请求的时间节点,请求的方式可以是请求服务器、缓存、本地资源等;
- responseStart 和 responseEnd 分别代表浏览器收到从服务器端(或缓存、本地资源)响应回的第一个字节和最后一个字节数据的时刻;
- 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文档完全解析完毕的时间节点;
- loadEventStart和loadEventEnd分别代表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
所以根据上面的时间点,我们可以计算常规的性能值,如下:使用该 api 时需要在页面完全加载完成之后才能使用,最简单的办法是在 window.onload 事件中读取各种数据,因为很多值必须在页面完全加载之后才能得出。
var timing = window.performance && window.performance.timing;
var navigation = window.performance && window.performance.navigation;
- 重定向次数:var redirectCount = navigation && navigation.redirectCount;
- 跳转耗时:var redirect = timing.redirectEnd - timing.redirectStart;
- APP CACHE 耗时:var appcache = Math.max(timing.domainLookupStart - timing.fetchStart, 0);
- DNS 解析耗时:var dns = timing.domainLookupEnd - timing.domainLookupStart;
- TCP 链接耗时:var conn = timing.connectEnd - timing.connectStart;
- 等待服务器响应耗时(注意是否存在cache):var request = timing.responseStart - timing.requestStart;
- 内容加载耗时(注意是否存在cache):var response = timing.responseEnd - timing.responseStart;
- 总体网络交互耗时,即开始跳转到服务器资源下载完成:var network = timing.responseEnd - timing.navigationStart;
- 渲染处理:var processing = (timing.domComplete || timing.domLoading) - timing.domLoading;
- 抛出 load 事件:var load = timing.loadEventEnd - timing.loadEventStart;
- 总耗时:var total = (timing.loadEventEnd || timing.loadEventStart || timing.domComplete || timing.domLoading) - timing.navigationStart;
- 可交互:var active = timing.domInteractive - timing.navigationStart;
- 请求响应耗时,即 T0,注意 cache:var t0 = timing.responseStart - timing.navigationStart;
- 首次出现内容,即 T1:var t1 = timing.domLoading - timing.navigationStart;
- 内容加载完毕,即 T3:var t3 = timing.loadEventEnd - timing.navigationStart;
2.2.3 Resource timing API
Resource timing API 是用来统计静态资源相关的时间信息,详细的内容请参考 W3C Resource timing( www.w3 组织网/TR/resource-timing/)。这里我们只介绍 performance.getEntries 方法,它可以获取页面中每个静态资源的请求,以百度移动版首页的 logo 为例如下:
比较有用的几个属性:
- name:资源的链接
- initiatorType:初始类型(注意这个类型并不准确,例如在 CSS 中的图片资源会这个值显示 CSS,所以还是推荐用 name 中的后缀名)
- duration:资源的总耗时(包括等待时长,请求时长,响应时长,相当于 responseEnd - startTime)
- transferSize:转换后的文件大小(略大于 encodedBodySize,为什么我取这个呢,因为这个值是和 chrome 的 devtool Network 里的 size 一致)
可以看到 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);
}
}
})();
<!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>
