├── .gitignore ├── README.md └── performance-timing.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # performance-timing 2 | 3 | performance-timing.js利用HTML5中的[navigation timing API](http://www.w3.org/TR/navigation-timing/)进行前端性能数据采集,是性能监控平台搭建的利器 4 | 5 | ### 浏览器兼容性 6 | 7 | 请参考:[http://caniuse.com/#feat=nav-timing](http://caniuse.com/#feat=nav-timing) 8 | 9 | ### 依赖 10 | 11 | 目前依赖[jQuery](http://jquery.com/),如果不希望依赖jQuery可以自己去实现函数`$.param`和`$.extend` 12 | 13 | ### 优势 14 | 15 | * 浏览器原生支持,准确性高 16 | * 能够获取到更多的数据,例如DNS解析时间、重定向时间等 17 | * 支持页面首次渲染时间的采集 18 | * 代码量少(百行左右) 19 | * 可定制化程度高 20 | * 支持抽样 21 | * 支持在网页加载完成后进行数据采集和发送,减少页面的性能损耗 22 | 23 | ### 使用示例 24 | 25 | ```js 26 | //支持 AMD 和 CMD,下面代码使用的是全局变量的方式 27 | Performance.start({ 28 | url: "http://www.xxx.com/a.gif", //后端收集数据的URL,必填 29 | rate: 0.2, //抽样比例,默认是10%抽样 30 | data: { //额外需要记录的数据,非必填 31 | ext_domain: "www.xxx.com", //域名,默认是document.domain 32 | ext_path: "_wap_index" //页面,默认是encodeURIComponent(location.pathname.toLowerCase().replace(/\//g, "_")); 33 | } 34 | }); 35 | ``` 36 | 37 | ### 采集的数据说明 38 | 39 | 默认情况下脚本将会采集以下数据(详细计算公式可参看采集脚本): 40 | 41 | |参数 |类型 |描述 | 42 | |:-------------:|:-------------:|:-----:| 43 | |`ext_domain` |string |域名 | 44 | |`ext_path`|string|页面| 45 | |`t_unload`|number|上个文档的卸载时间| 46 | |`t_redirect`|number|页面重定向的时间| 47 | |`t_dns`|number|DNS解析时间| 48 | |`t_tcp`|number|服务器连接时间| 49 | |`t_request`|number|服务器响应时间| 50 | |`t_response`|number|网页文档下载时间| 51 | |`t_paint`|number|首次渲染时间| 52 | |`t_dom`|number|DOM Ready时间(阶段)| 53 | |`t_domready`|number|DOM Ready时间(总和)| 54 | |`t_load`|number|onload时间(阶段)| 55 | |`t_onload`|number|onload时间(总和)| 56 | |`t_white`|number|白屏时间| 57 | |`t_all`|number|全部过程时间| 58 | 59 | 60 | ### 注意事项 61 | 62 | * 后端利用数据进行统计时,需要排除值为0的项,避免某些不准确的数据或者未采集到的数据对性能指标产生影响 63 | * 用户IP以及浏览器识别的数据可以放到后端进行采集 64 | -------------------------------------------------------------------------------- /performance-timing.js: -------------------------------------------------------------------------------- 1 | /* 2 | * performance-timing.js 0.2 3 | * https://github.com/tjuking/performance-timing 4 | */ 5 | 6 | (function (global, factory) { 7 | 8 | "use strict"; 9 | 10 | if (typeof define === "function" && define.amd) { 11 | define(["jquery"], function ($) { 12 | return factory($, global); 13 | }); 14 | } else if (typeof exports !== "undefined") { 15 | module.exports = factory(require("jquery"), global); 16 | } else { 17 | global.Performance = factory(jQuery, global); 18 | } 19 | 20 | })(typeof window !== "undefined" ? window : this, function ($, window) { 21 | 22 | "use strict"; 23 | 24 | //封装的Performance对象 25 | var _P = { 26 | 27 | //程序配置参数 28 | options: { 29 | url: "", //后端收集数据的URL(必须) 30 | rate: 0.1, //抽样比例(必须,默认为10%抽中) 31 | data: { //额外需要发送的数据(非必须) 32 | "ext_domain": encodeURIComponent(document.domain), 33 | "ext_path": encodeURIComponent(window.location.pathname.toLowerCase().replace(/\//g, "_")) 34 | } 35 | }, 36 | 37 | //原始timing数据 38 | timing: {}, 39 | 40 | //用于存储解析后的数据 41 | data: {}, 42 | 43 | //检查是否支持navigation timing api,并且包含在抽样中 44 | check: function () { 45 | return window.performance && window.performance.timing && Math.random() < _P.options.rate; 46 | }, 47 | 48 | //程序的处理(包括数据收集、发送动作) 49 | setup: function () { 50 | _P.timing = window.performance.timing; 51 | //数据正常时才发送 52 | if (_P.setData(_P.timing)) { 53 | _P.send(_P.options.url, _P.data); 54 | } 55 | }, 56 | 57 | //设置需要发送给后端的数据 58 | setData: function (timing) { 59 | var startTime = timing.navigationStart || timing.fetchStart; 60 | var data = { 61 | "t_unload": timing.unloadEventEnd - timing.unloadEventStart, //上个文档的卸载时间 62 | "t_redirect": timing.redirectEnd - timing.redirectStart, //*重定向时间 63 | "t_dns": timing.domainLookupEnd - timing.domainLookupStart, //*DNS查询时间 64 | "t_tcp": timing.connectEnd - timing.connectStart, //*服务器连接时间 65 | "t_request": timing.responseStart - timing.requestStart, //*服务器响应时间 66 | "t_response": timing.responseEnd - timing.responseStart, //*网页下载时间 67 | "t_paint": _P.getFirstPaintTime() - startTime, //*首次渲染时间 68 | "t_dom": timing.domContentLoadedEventStart - timing.domLoading, //dom ready时间(阶段) 69 | "t_domready": timing.domContentLoadedEventStart - startTime, //*dom ready时间(总和) 70 | "t_load": timing.loadEventStart - timing.domLoading, //onload时间(阶段) 71 | "t_onload": timing.loadEventStart - startTime, //*onload时间(总和) 72 | "t_white": timing.responseStart - startTime, //*白屏时间 73 | "t_all": timing.loadEventEnd - startTime //整个过程的时间之和 74 | }; 75 | for (var key in data) { 76 | //删除无用数据,避免干扰(小于等于0或大于两分钟) 77 | if (data[key] <= 0 || data[key] >= 120000) { 78 | delete data[key]; 79 | } 80 | } 81 | //合并程序外传入的数据 82 | $.extend(true, _P.data, data, _P.options.data); 83 | return startTime > 0; 84 | }, 85 | 86 | //获取首次渲染时间 87 | getFirstPaintTime: function () { 88 | var firstPaintTime = 0; 89 | if (window.chrome && typeof window.chrome.loadTimes === "function") { //Chrome 90 | firstPaintTime = window.chrome.loadTimes().firstPaintTime * 1000; 91 | } else if (typeof _P.timing.msFirstPaint === "number") { //IE 92 | firstPaintTime = _P.timing.msFirstPaint; 93 | } 94 | return Math.round(firstPaintTime); 95 | }, 96 | 97 | //发送数据到后端 98 | send: function (url, data) { 99 | var img = new Image(); 100 | img.src = url + "?" + $.param(data); 101 | }, 102 | 103 | //程序主入口 104 | start: function (options) { 105 | //合并参数 106 | $.extend(true, _P.options, options); 107 | //支持API并且被抽样抽中 108 | if (_P.check()) { 109 | //是否已经形成数据(页面加载完成之后) 110 | if (window.performance.timing.loadEventEnd > 0) { 111 | _P.setup(); 112 | } else { 113 | $(window).on("load", function () { 114 | //不能影响最后的时间计算 115 | window.setTimeout(function () { 116 | _P.setup(); 117 | }, 0); 118 | }); 119 | } 120 | } 121 | } 122 | 123 | }; 124 | 125 | return _P; 126 | }); --------------------------------------------------------------------------------