2

从一次实验学习性能优化

Web API之Performance 接口详解

下图是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包含了页面加载时间相关的性能信息。

    • 重要的参数:

    1. navigationStart:准备加载新页面的起始时间,一般认为是页面最初的时间.一般和fetchStart值相等,和connectEnd中间的时间用于DNS解析,建立TCP连接.

    2. requestStart:返回从服务器、缓存、本地资源等,开始请求文档的时间,一般用于统计网络资源请求的时间.

    3. domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的readystatechange事件触发时)的时间,与domComplete对应,用于统计页面渲染时间.

    4. domComplete:返回当前网页DOM结构生成时间,此时页面渲染完成.

    5. DNS查询耗时 :domainLookupEnd - domainLookupStart

    6. TCP链接耗时 :connectEnd - connectStart

    7. request请求耗时 :responseEnd - responseStart

    8. 解析 DOM 树结构的时间:domComplete - responseEnd;

    9. 一般白屏时间:responseStart - navigationStart

    10. 页面总耗时: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万个微博签到数据来测试两者的性能差异.

  1. 一开始在两个单独文件中分别使用Echarts和Highcharts来绘制几百个点,发现由于网络,引入的库不同,二者时间不具有对比性.因此转而在同一页面中绘制.

  2. 将所有依赖在head中引入,分别封装两个绘图函数,用ajax从远程获取数据,在回调函数中绘图并且统计时间,从而分析性能差异.

  3. 测试代码:

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个点:100点
Echarts画10万个点,highcharts画3000个点:]图片描述
Echarts单独画10万个点:图片描述
highcharts单独画3000个点:图片描述

总结:实验结果很容易预测,canvas肯定比基于dom的svg性能好得多,而且如果使用webGL,利用显卡加速,性能会进一步提升.但是测试中遇到很多有价值的问题,例如如何利用js获取页面性能信息,从而做出优化策略,如何控制变量排除干扰因素使得测试更具有说服力.实验中对performance以及面板的深入了解也使得我对页面整个渲染流程有了更深的认识.


逺方小鎭
1.3k 声望165 粉丝