window.performance 是W3C性能小组引入的新的API,目前IE9以上的浏览器都支持。一个performance对象的完整结构如下图所示:
Window.performance
虽然叫 Timing API
但是用起来却是 window.performance
1
2
3
4// 兼容性写法
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的整体结构如上图所示:
各字段的含义如下:
- startTime:有些浏览器实现为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事件中读取各种数据,因为很多值必须在页面完全加载之后才能得出。)
1 | var timing = window.performance && window.performance.timing; |
2.2.3 Resource timing API
Resource timing API是用来统计静态资源相关的时间信息,详细的内容请参考W3C 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的使用场景,感兴趣的同学可以深入研究。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68;
(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>
性能监控,数据上报
1 | function WBP(option) { |
数据上报字符串加密解密
1 | /*** |
仿百度上报
1 | router.afterEach( ( to, from, next ) => { |
js 判断 wifi and ‘2g’, ‘3g’, ‘4g’
1 | var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || { tyep: 'unknown' }; |
设备选型
1 | function getInfo(){ |