前端性能监控
前端设计
白屏时间(first Paint Time)——用户从打开页面开始到页面开始有东西呈现为止
如何获取:
1、chrome 高版本:
window.chrome.loadTimes().firstPaintTime loadTimes获取的结果。
2、其他版本的浏览器
头部资源加载时间结束时间-首字节时间开始时间
var firstPaintTime = end_time - performance.timing.navigationStart
performance.timing.navigationStart首字节时间开始时间是由浏览器提供的函数
end_time需要我们计算
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"/> <script> var start_time = +new Date; //测试时间起点,实际统计起点为 DNS 查询 </script> <!-- 3s 后这个 js 才会返回 --> <script src="script.php"></script> <script> var end_time = +new Date; //时间终点 var headtime = end_time - start_time; //头部资源加载时间 console.log(headtime); </script> </head> <body> <p>在头部资源加载完之前页面将是白屏</p> <p>script.php 被模拟设置 3s 后返回,head 底部内嵌 JS 等待前面 js 返回后才执行</p> <p>script.php 替换成一个执行长时间循环的 js 效果也一样</p> </body> </html>
首屏时间——用户浏览器首屏内所有内容都呈现出来所花费的时间
首屏时间的统计比较复杂,因为涉及图片等多种元素及异步渲染等方式。观察加载视图可发现,影响首屏的主要因素的图片的加载。通过统计首屏内图片的加载时间便可以获取首屏渲染完成的时间。统计流程如下:
首屏位置调用 API 开始统计 -> 绑定首屏内所有图片的 load 事件 -> 页面加载完后判断图片是否在首屏内,找出加载最慢的一张 -> 首屏时间
这是同步加载情况下的简单统计逻辑,另外需要注意的几点:
- 页面存在 iframe 的情况下也需要判断加载时间
- gif 图片在 IE 上可能重复触发 load 事件需排除
- 异步渲染的情况下应在异步获取数据插入之后再计算首屏
- css 重要背景图片可以通过 JS 请求图片 url 来统计(浏览器不会重复加载)
- 没有图片则以统计 JS 执行时间为首屏,即认为文字出现时间
//IE gif重复onload解决 var img=new Image(); img.load=function(){ //do something img.load=null;//重新赋值为null } img.src='××.gif';
统计方法1:
原理:在首屏渲染之前埋上处理逻辑,使用定时器不断的去检测img节点的图片。判断图片是否在首屏和加载完成,找到首屏中加载时间最慢的的图片完成的时间,从而计算出首屏时间。如果首屏有没有图片,如果没图片就用domready时间。
缺点: 1.浏览器定时器最大精度为55ms 2.背景图片加载没有计算在内 3.不断检测并执行的脚本耗时
/1,获取首屏基线高度 //2,计算出基线dom元素之上的所有图片元素 //3,所有图片onload之后为首屏显示时间 function getOffsetTop(ele) { var offsetTop = ele.offsetTop; if (ele.offsetParent !== null) { offsetTop += getOffsetTop(ele.offsetParent); } return offsetTop; } var firstScreenHeight = win.screen.height; var firstScreenImgs = []; var isFindLastImg = false; var allImgLoaded = false; var t = setInterval(function() { var i, img; if (isFindLastImg) { if (firstScreenImgs.length) { for (i = 0; i < firstScreenImgs.length; i++) { img = firstScreenImgs[i]; if (!img.complete) { allImgLoaded = false; break; } else { allImgLoaded = true; } } } else { allImgLoaded = true; } if (allImgLoaded) { collect.add({ firstScreenLoaded: startTime - Date.now() }); clearInterval(t); } } else { var imgs = body.querySelector('img'); for (i = 0; i < imgs.length; i++) { img = imgs[i]; var imgOffsetTop = getOffsetTop(img); if (imgOffsetTop > firstScreenHeight) { isFindLastImg = true; break; } else if (imgOffsetTop <= firstScreenHeight && !img.hasPushed) { img.hasPushed = 1; firstScreenImgs.push(img); } } } }, 0); doc.addEventListener('DOMContentLoaded', function() { var imgs = body.querySelector('img'); if (!imgs.length) { isFindLastImg = true; } }); win.addEventListener('load', function() { allImgLoaded = true; isFindLastImg = true; if (t) { clearInterval(t); } collect.log(collect.global); });
统计方法2:
原理:对于网页高度小于屏幕的网站来说,只要在页面底部加上脚本打印当前时间即可;或者对于网页高度大于一屏的网页来说,只要在估算接近于一屏幕的元素的位置后,打印一下当前时间。当然这个时间要得把首屏中所有图片的加载时间也算上。
缺点: 1.需要每个页面手动加入到对应位置 2.背景图片加载没有计算在内
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0"> <script type="text/javascript"> window.logInfo = {}; window.logInfo.openTime = performance.timing.navigationStart; </script> </head> <body> <div>这是第一屏,这是第一屏</div> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> <div>第一屏结尾,第一屏结尾</div> <script type="text/javascript"> (function logFirstScreen() { var images = document.getElementsByTagName('img'); var iLen = images.length; var curMax = 0; var inScreenLen = 0; // 图片的加载回调 function imageBack() { this.removeEventListener && this.removeEventListener('load', imageBack, !1); if (++curMax === inScreenLen) { // 如果所有在首屏的图片均已加载完成了的话,发送日志 log(); } } // 对于所有的位于指定区域的图片,绑定回调事件 for (var s = 0; s < iLen; s++) { var img = images[s]; var offset = { top: 0 }; var curImg = img; while (curImg.offsetParent) { offset.top += curImg.offsetTop; curImg = curImg.offsetParent; } // 判断图片在不在首屏 if (document.documentElement.clientHeight < offset.top) { continue; } // 图片还没有加载完成的话 if (!img.complete) { inScreenLen++; img.addEventListener('load', imageBack, !1); } } // 如果首屏没有图片的话,直接发送日志 if (inScreenLen === 0) { log(); } // 发送日志进行统计 function log () { window.logInfo.firstScreen = +new Date() - window.logInfo.openTime; console.log('首屏时间:', window.logInfo.firstScreen + 'ms'); } })(); </script> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png"> </body> </html>
用户可操作时间(dom Interactive)——用户可以进行正常的点击、输入等操作,默认可以统计domready时间,因为通常会在这时候绑定事件操作
用户可操作为所有DOM都解析完毕的时间,默认可以统计domready时间,因为通常会在这时候绑定事件操作。对于使用了模块化异步加载的 JS 可以在代码中去主动标记重要 JS 的加载时间,这也是产品指标的统计方式。
使用jquery中的$(document).ready()即是此意义 window.performance.timing.domInteractive window.performance.timing.domContentLoadedEventStart
计算公式:
performance.timing.domInteractive - performance.timing.navigationStart
总下载时间——页面所有资源都加载完成并呈现出来所花的时间,即页面 onload 的时间
默认可以统计onload时间,这样可以统计同步加载的资源全部加载完的耗时。如果页面中存在很多异步渲染,可以将异步渲染全部完成的时间作为总下载时间。
计算公式:
performance.timing.loadEventStart- performance.timing.navigationStart
后端设计
1、报表展示
将各项指标展示在PC端浏览器界面上。
2、图表展示
可以采用D3.js或者百度的Echart对分析结果进行图表展示。