从一次实验学习性能优化
Web API之Performance 接口详解
下图是Performance 接口的属性,提供给定页面的与时间相关的性能信息.
-
navigation 包含了页面浏览上下文的导航信息,比如大量获取资源的重定向。
redirectCount表示页面加载前经过重定向次数,该接口有同源策略限制,即仅能检测同源的重定向。
返回值应该是0,1,2,255中的一个。分别对应三个枚举值:
0 : TYPE_NAVIGATE (用户通过常规方式访问页面,比如点一个链接,输入地址等) 1 : TYPE_RELOAD (用户通过刷新,包括JS调用刷新接口(Location.reload())等方式访问页面) 2 : TYPE_BACK_FORWARD (用户通过浏览器历史记录访问本页面) 255: 其他方式
memory包含了堆栈使用情况信息,usedJSHeapSize表示所有被使用的js堆栈内存;totalJSHeapSize表示当前js堆栈内存总大小,这表示usedJSHeapSize不能大于totalJSHeapSize。
-
timing包含了页面加载时间相关的性能信息。
重要的参数:
navigationStart:准备加载新页面的起始时间,一般认为是页面最初的时间.一般和fetchStart值相等,和connectEnd中间的时间用于DNS解析,建立TCP连接.
requestStart:返回从服务器、缓存、本地资源等,开始请求文档的时间,一般用于统计网络资源请求的时间.
domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的readystatechange事件触发时)的时间,与domComplete对应,用于统计页面渲染时间.
domComplete:返回当前网页DOM结构生成时间,此时页面渲染完成.
DNS查询耗时 :domainLookupEnd - domainLookupStart
TCP链接耗时 :connectEnd - connectStart
request请求耗时 :responseEnd - responseStart
解析 DOM 树结构的时间:domComplete - responseEnd;
一般白屏时间:responseStart - navigationStart
页面总耗时:loadEventEnd/domComplete - navigationStart
一张timing顺序图供参考:
navigationStart:当前浏览器窗口的前一个网页关闭,发生unload事件时的Unix毫秒时间戳。如果没有前一个网页,则等于fetchStart属性。 unloadEventStart:如果前一个网页与当前网页属于同一个域名,则返回前一个网页的unload事件发生时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0。 unloadEventEnd:如果前一个网页与当前网页属于同一个域名,则返回前一个网页unload事件的回调函数结束时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0。redirectStart:返回第一个HTTP跳转开始时的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0。redirectEnd:返回最后一个HTTP跳转结束时(即跳转回应的最后一个字节接受完成时)的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0。fetchStart:返回浏览器准备使用HTTP请求读取文档时的Unix毫秒时间戳。该事件在网页查询本地缓存之前发生。domainLookupStart:返回域名查询开始时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。domainLookupEnd:返回域名查询结束时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。 connectStart:返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。 connectEnd:返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。 secureConnectionStart:返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。 requestStart:返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。 responseStart:返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。 responseEnd:返回浏览器从服务器收到(或从本地缓存读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。 domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的readystatechange事件触发时)的Unix毫秒时间戳。 domInteractive:返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。 domContentLoadedEventStart:返回当前网页DOMContentLoaded事件发生时(即DOM结构解析完毕、所有脚本开始运行时)的Unix毫秒时间戳。 domContentLoadedEventEnd:返回当前网页所有需要执行的脚本执行完成时的Unix毫秒时间戳。 domComplete:返回当前网页DOM结构生成时(即Document.readyState属性变为“complete”,以及相应的readystatechange事件发生时)的Unix毫秒时间戳。 loadEventStart:返回当前网页load事件的回调函数开始时的Unix毫秒时间戳。如果该事件还没有发生,返回0。 loadEventEnd:返回当前网页load事件的回调函数运行结束时的Unix毫秒时间戳。如果该事件还没有发生,返回0。
-
performance的方法:
performance.now()返回当前网页自从performance.timing.navigationStart到当前时间之间的微秒数(毫秒的千分之一)。精度可以达到100万分之一秒。利用performance.now方法,可以得到某种操作消耗的准确时间。
performance.mark('mark') performance.mark('markEnd') performance.measure('name', 'mark', 'markEnd') // 清除指定标记 performance.clearMarks('mark'); // 清除所有标记 performance.clearMarks();
performance.mark()用于标记某个时间点。使用该方法参数(即标记时间点),再调用 performance.measure(name, nameStart, nameEnd);即可测得某两个时间点之间的耗时.
var start = performance.now(); // 被测代码 var end = performance.now(); console.log('耗时:' + (end - start) + '微秒。');
performance.getEntries() 资源测速:该方法以数组形式,返回请求的时间统计信息(脚本文件、样式表、图片文件等等),有多少个请求,返回数组就会有多少个成员。单位是微秒(microsecond)
// 统计样式,脚本,图片请求数和消耗时间 var imgResource = { count: 0, time: 0 }; var cssResource = { count: 0, time: 0 }; var scriptResource = { count: 0, time: 0 }; performance.getEntries().forEach(item => { if (item.initiatorType === 'img') { imgResource.count++; imgResource.time += item.duration } else if (item.initiatorType === 'link') { cssResource.count++; cssResource.time += item.duration } else if (item.initiatorType === 'script') { scriptResource.count++; scriptResource.time += item.duration } });
Canvas和svg
Canvas基于像素,提供2D绘制函数,是一种HTML元素类型,依赖于HTML,通过脚本绘制图形;绘制即时模式图形,适合像素处理,动态渲染和大数据量绘制.
SVG基于矢量,提供一系列图形元素(Rect, Path, Circle, Line …),还有完整的动画,事件机制,能独立使用,也可以嵌入到HTML中.SVG 是一个保留在内存中模型中的保留模式图形模型,而内存中模型可通过重新呈现的代码结果进行操作,更适合用来做动态交互.
实际对比:Echarts和Highcharts
Echarts基于Canvas,而Highcharts基于SVG,本次实验利用10万个微博签到数据来测试两者的性能差异.
一开始在两个单独文件中分别使用Echarts和Highcharts来绘制几百个点,发现由于网络,引入的库不同,二者时间不具有对比性.因此转而在同一页面中绘制.
将所有依赖在head中引入,分别封装两个绘图函数,用ajax从远程获取数据,在回调函数中绘图并且统计时间,从而分析性能差异.
测试代码:
Echarts函数
function renderEchart(weiboData) { var timeStart = window.performance.now().toFixed(4); $('.eRender span:eq(0)').html($('.eRender span:eq(0)').html() + timeStart); var myChart = echarts.init(document.getElementById('main')); myChart.setOption(option); var timeEnd = window.performance.now().toFixed(4); console.log(timeEnd - timeStart); $('.eRender span:eq(1)').html($('.eRender span:eq(1)').html() + timeEnd); $('.eRender span:eq(2)').html($('.eRender span:eq(2)').html() + (timeEnd - timeStart).toFixed(4) + 'ms');}
Highcharts函数
function renderHchart(hda) { var timeStart = window.performance.now().toFixed(4); $('.hRender span:eq(0)').html($('.hRender span:eq(0)').html() + timeStart); var H = Highcharts, map = H.maps['cn/china'], chart; var colors = Highcharts.getOptions().colors; new Highcharts.Map('container',params) var timeEnd = window.performance.now().toFixed(4); console.log(timeEnd - timeStart); $('.hRender span:eq(1)').html($('.hRender span:eq(1)').html() + timeEnd); $('.hRender span:eq(2)').html($('.hRender span:eq(2)').html() + (timeEnd - timeStart).toFixed(4) + 'ms');}
4.测试结果:由于svg无法画出10万个点(浏览器会卡死),画3000点就需要7s.所以下面svg最多只画3000个点.
画100个点:Echarts画10万个点,highcharts画3000个点:]Echarts单独画10万个点:highcharts单独画3000个点:总结:实验结果很容易预测,canvas肯定比基于dom的svg性能好得多,而且如果使用webGL,利用显卡加速,性能会进一步提升.但是测试中遇到很多有价值的问题,例如如何利用js获取页面性能信息,从而做出优化策略,如何控制变量排除干扰因素使得测试更具有说服力.实验中对performance以及面板的深入了解也使得我对页面整个渲染流程有了更深的认识.