Nodejs中分析web前端性能(window.performance)

在nodejs中,通过puppeteer来获取web页面中的window.performance对象,从而分析页面的性能。下面直接上代码。

专注于为中小企业提供成都网站建设、成都网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业湾里免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上千企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

const puppeteer = require('puppeteer');
const path = require("path");

const logger=require("./log");
const log = logger.getPuppeteerRecordLogger() ;

/*
    启动浏览器
*/ 
async function launchBrowser(){
    //启动浏览器实例 [puppeteer.createBrowserFetcher([options])]
  let browser = await puppeteer.launch({
    // 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
    //executablePath: '/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium',
    //如果是访问https页面 此属性会忽略https错误
    ignoreHTTPSErrors: true,
    // 关闭headless模式, 不会打开浏览器
    headless: true,
    //浏览器启动参数 https://peter.sh/experiments/chromium-command-line-switches/   --timeout
    args:['--disk-cache-size=0','--disable-cache','--disable-infobars','--window-size=800,600','--ignore-certificate-errors','--enable-feaures'],
    //是否为每个选项卡自动打开DevTools面板。 如果此选项为true,则headless选项将设置为false。
    devtools: false,
    //Defaults to 30000 (30 seconds). Pass 0 to disable timeout.
    timeout: 0
    //放慢puppeteer执行的动作,方便调试
    //slowMo: 250
  });
  return browser ;
}

async function saveHarlog(url,dirPath,filename){
    let homesite = url ;
    //保存的文件路径
    let harFilePath = path.join(dirPath,filename) ;
    //处理URL
    if(!(url.startsWith('http://') || url.startsWith('https://'))){
        url = "http://" + url ;
    }

  //打开浏览器
  let browser = await launchBrowser() ;

  //创建一个新页面
  //let page = await browser.newPage();
  const page = (await browser.pages())[0];
  try{      
    await page.goto(url,{
            timeout:0
    });

    /*
        在page上下文中得到window.performance.timing
    */
      const timing = await page.evaluate( _ => {
      const {navigationStart,unloadEventStart,unloadEventEnd,
        redirectStart,redirectEnd,fetchStart,domainLookupStart,
        domainLookupEnd,connectStart,connectEnd,secureConnectionStart,
        requestStart,responseStart,responseEnd,domLoading,domInteractive,
        domContentLoadedEventStart,domContentLoadedEventEnd,domComplete,
        loadEventStart,loadEventEnd} = window.performance.timing;
      return ({navigationStart:navigationStart,
                        unloadEventStart:unloadEventStart,
                        unloadEventEnd:unloadEventEnd,
                        redirectStart:redirectStart,
                        redirectEnd:redirectEnd,
                        fetchStart:fetchStart,
                        domainLookupStart:domainLookupStart,
                        domainLookupEnd:domainLookupEnd,
                        connectStart:connectStart,
                        connectEnd:connectEnd,
                        secureConnectionStart:secureConnectionStart,
                        requestStart:requestStart,
                        responseStart:responseStart,
                        responseEnd:responseEnd,
                        domLoading:domLoading,
                        domInteractive:domInteractive,
                        domContentLoadedEventStart:domContentLoadedEventStart,
                        domContentLoadedEventEnd:domContentLoadedEventEnd,
                        domComplete:domComplete,
                        loadEventStart:loadEventStart,
                        loadEventEnd:loadEventEnd})
        })
        log.info('--->' + JSON.stringify(timing)) ;

    if(timing){
        //long 型的毫秒数。上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和fetchStart相同。
        let navigationStart  = timing.navigationStart ;
        //long 型的毫秒数.表征了unload事件抛出时的UNIX时间戳,如果没有上一个文档, 这个值会返回0
        let unloadEventStart  = timing.unloadEventStart ;
        //long 型的毫秒数,表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档, 这个值会返回0
        let unloadEventEnd  = timing.unloadEventEnd ;
        //long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.
        let redirectStart   = timing.redirectStart  ;
        //long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.
        let redirectEnd  = timing.redirectEnd  ;
        //long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。
        let fetchStart  = timing.fetchStart  ;
        //long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和fetchStart一致。
        let domainLookupStart  = timing.domainLookupStart  ;
        //long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 fetchStart一致。
        let domainLookupEnd  = timing.domainLookupEnd  ;
        //long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。
        let connectStart   = timing.connectStart   ;
        //long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。
        let connectEnd   = timing.connectEnd   ;
        //long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0
        let secureConnectionStart  = timing.secureConnectionStart    ;
        //long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳
        let requestStart   = timing.requestStart ;
        //long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。
        let responseStart  = timing.responseStart  ;
        //long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
        let responseEnd  = timing.responseEnd  ;
        //long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。
        let domLoading  = timing.domLoading  ;
        //long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳
        let domInteractive = timing.domInteractive  ;
        //ong 型的毫秒数,返回当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳
        let domContentLoadedEventStart = timing.domContentLoadedEventStart  ;
        //long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳
        let domContentLoadedEventEnd = timing.domContentLoadedEventEnd  ;
        //long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳
        let domComplete = timing.domComplete  ;
        //long 型的毫秒数,返回该文档下,load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0
        let loadEventStart = timing.loadEventStart  ;
        //long 型的毫秒数,返回当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0
        let loadEventEnd = timing.loadEventEnd  ;

        //呈现了如何导航到当前文档的信息
        //let navigation = performance.navigation ;
    }   
  }catch(error){
    log.info('resovle error :' + url + ";  error message:" + error) ;
  }finally{
    if(browser){
        await browser.close();      
    }
  } 
}

exports.launchBrowser = launchBrowser;
exports.saveHarlog = saveHarlog;

指标说明

//@param t -> timing
async function getPerformanceTiming (t) { 
    if (!t) {
        log.info('not allow null');
        return;
    }
    var times = {};

    //【重要】页面加载完成的时间
    //【原因】这几乎代表了用户等待页面可用的时间
    times.loadPage = t.loadEventEnd - t.navigationStart;

    //【重要】解析 DOM 树结构的时间
    //【原因】反省下你的 DOM 树嵌套是不是太多了!
    times.domReady = t.domComplete - t.responseEnd;

    //【重要】重定向的时间
    //【原因】拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
    times.redirect = t.redirectEnd - t.redirectStart;

    //【重要】DNS 查询时间
    //【原因】DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
    // 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)           
    times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;

    //【重要】读取页面第一个字节的时间
    //【原因】这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加cdn 处理了么?加带宽了么?加 CPU 运算速度了么?
    // TTFB 即 Time To First Byte 的意思
    // 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
    times.ttfb = t.responseStart - t.navigationStart;

    //【重要】内容加载完成的时间
    //【原因】页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
    times.request = t.responseEnd - t.requestStart;

    //【重要】执行 onload 回调函数的时间
    //【原因】是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
    times.loadEvent = t.loadEventEnd - t.loadEventStart;

    // DNS 缓存时间
    times.appcache = t.domainLookupStart - t.fetchStart;

    // 卸载页面的时间
    times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;

    // TCP 建立连接完成握手的时间
    times.connect = t.connectEnd - t.connectStart;

    return times;
}

本文名称:Nodejs中分析web前端性能(window.performance)
网页网址:http://ybzwz.com/article/jjejsp.html