前端性能指标,大多有TTFB ,首屏,首次可交互时间等
相关的文章已经有很多,细节这里就不多说了,可参考文末资料
总体来说,需要知道浏览器(新的)给我们提供了 Performance API,使用这个属性,我们可以得到一系列跟性能相关的数据
结合各个时间点的意义,我们可以计算出关键的耗时指标
看看下面这张图
经过简单的计算,可以获取到这样的信息
与DevTools 的Network来比较,数据是差不多的,应该能作为参考
简单上报一下
那么,这些个指标是怎末计算的呢,且看代码部分,看看注释应该就知道了
如何计算这些时间点,因人而异,各人有不同的版本,只要觉得合理,其实都是可以的
1 <script>
2 ;(function(window, undefined) {
3 function addEvent(type, fn) {
4 if (window.addEventListener) {
5 window.addEventListener(type, fn, false);
6 } else {
7 window.attachEvent('on' + type, fn);
8 }
9 }
10
11 // load 事件触发猴再收集相关数据
12 addEvent('load', getTiming);
13
14 // 快捷入口
15 window.getTiming = getTiming;
16
17 function getTiming() {
18 // 浏览器支持的API
19 var performance = window.performance;
20
21 if (!performance || !performance.timing) {
22 console.log('当前浏览器不支持Performance Time API');
23 return;
24 }
25
26 var timing = performance.timing,
27 timings = {
28 page: [],
29 pageReport: []
30 };
31
32 // 设置某一项耗时统计
33 function setPageTiming(name, text, time) {
34 timings.page.push({
35 name: name,
36 text: text,
37 time: time + ' ms'
38 });
39
40 timings.pageReport.push({
41 name: name,
42 time: time + ' ms'
43 });
44 }
45
46 // 页面加载仍未完成,loadEventEnd值可能为0,需要延迟处理
47 if (timing.loadEventEnd - timing.navigationStart < 0) {
48 console.log('getTiming delay');
49 setTimeout(getTiming, 1000);
50 return;
51 }
52
53 setPageTiming('redirect', '重定向', timing.redirectEnd - timing.redirectStart);
54 setPageTiming('dns', 'DNS解析', timing.domainLookupEnd - timing.domainLookupStart);
55 setPageTiming('tcp', 'TCP连接', timing.connectEnd - timing.connectStart);
56 setPageTiming('ssl', 'TCP-SSL连接', !timing.secureConnectionStart ? 0 : timing.connectEnd - timing.secureConnectionStart);
57 setPageTiming('ttfb', 'TTFB服务器返回首个字节', timing.responseStart - timing.requestStart);
58 setPageTiming('download', 'DOM资源下载', timing.responseEnd - timing.responseStart);
59 setPageTiming('before-parse-html', 'DOM解析前耗时', timing.responseEnd - timing.navigationStart);
60 setPageTiming('html-parsed', 'DOM解析完成', timing.domInteractive - timing.domLoading);
61 setPageTiming('dom-loaded', 'DOM加载完成', timing.domComplete - timing.domLoading);
62 setPageTiming('dom-content-load', 'DOMContentLoaded事件开始', timing.domContentLoadedEventStart - timing.navigationStart);
63 setPageTiming('dom-content-loaded', 'DOMContentLoaded事件结束', timing.domContentLoadedEventEnd - timing.navigationStart);
64 setPageTiming('load', 'Load事件开始', timing.loadEventStart - timing.navigationStart);
65 setPageTiming('loaded', 'Load事件结束', timing.loadEventEnd - timing.navigationStart);
66
67 // 以下两项可以直接获取
68 var paintTiming = {
69 'first-paint': '首屏(首次绘制)',
70 'first-contentful-paint': '首屏(首次内容绘制)'
71 };
72
73 performance.getEntriesByType('paint').forEach(function(entry) {
74 if (paintTiming[entry.name]) {
75 setPageTiming(entry.name, paintTiming[entry.name], entry.startTime + entry.duration);
76 }
77 });
78
79 // 以下两项指标与需求有关,需要根据业务要求在代码中打点统计
80 // setPageTiming('first-meaningful-paint', '首屏(首次有效绘制)', timing.domInteractive - timing.navigationStart);
81 // setPageTiming('time-to-interactive', '可交互时间', timing.domInteractive - timing.navigationStart);
82
83 // 简易版统计上报
84 if (requestIdleCallback) {
85 requestIdleCallback(function() {
86 report(timings.pageReport);
87 });
88 } else {
89 setTimeout(function() {
90 report(timings.pageReport);
91 });
92 }
93
94 function report(data) {
95 data = data || [];
96
97 if (!data.length) {
98 return;
99 }
100
101 (new Image()).src = 'https://a.b.c/d/e?t=' + (new Date())
102 + '&l=' + location.href
103 + '&p=' + navigator.platform
104 + '&net=' + navigator.connection.effectiveType
105 + '&u=' + navigator.userAgent
106 + '×=' + JSON.stringify(data);
107 }
108
109 console.table(timings.page);
110 console.log(performance.timing);
111 }
112 })(window);
113 </script>
参考资料: