您好,登錄后才能下訂單哦!
在nodejs中,通過puppeteer來獲取web頁面中的window.performance對(duì)象,從而分析頁面的性能。下面直接上代碼。
const puppeteer = require('puppeteer');
const path = require("path");
const logger=require("./log");
const log = logger.getPuppeteerRecordLogger() ;
/*
啟動(dòng)瀏覽器
*/
async function launchBrowser(){
//啟動(dòng)瀏覽器實(shí)例 [puppeteer.createBrowserFetcher([options])]
let browser = await puppeteer.launch({
// 若是手動(dòng)下載的chromium需要指定chromium地址, 默認(rèn)引用地址為 /項(xiàng)目目錄/node_modules/puppeteer/.local-chromium/
//executablePath: '/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium',
//如果是訪問https頁面 此屬性會(huì)忽略https錯(cuò)誤
ignoreHTTPSErrors: true,
// 關(guān)閉headless模式, 不會(huì)打開瀏覽器
headless: true,
//瀏覽器啟動(dòng)參數(shù) 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'],
//是否為每個(gè)選項(xiàng)卡自動(dòng)打開DevTools面板。 如果此選項(xiàng)為true,則headless選項(xiàng)將設(shè)置為false。
devtools: false,
//Defaults to 30000 (30 seconds). Pass 0 to disable timeout.
timeout: 0
//放慢puppeteer執(zhí)行的動(dòng)作,方便調(diào)試
//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() ;
//創(chuàng)建一個(gè)新頁面
//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 型的毫秒數(shù)。上一個(gè)文檔卸載(unload)結(jié)束時(shí)的UNIX時(shí)間戳。如果沒有上一個(gè)文檔,這個(gè)值會(huì)和fetchStart相同。
let navigationStart = timing.navigationStart ;
//long 型的毫秒數(shù).表征了unload事件拋出時(shí)的UNIX時(shí)間戳,如果沒有上一個(gè)文檔, 這個(gè)值會(huì)返回0
let unloadEventStart = timing.unloadEventStart ;
//long 型的毫秒數(shù),表征了unload事件處理完成時(shí)的UNIX時(shí)間戳。如果沒有上一個(gè)文檔, 這個(gè)值會(huì)返回0
let unloadEventEnd = timing.unloadEventEnd ;
//long 型的毫秒數(shù),表征了第一個(gè)HTTP重定向開始時(shí)的UNIX時(shí)間戳。如果沒有重定向,或者重定向中的一個(gè)不同源,這個(gè)值會(huì)返回0.
let redirectStart = timing.redirectStart ;
//long 型的毫秒數(shù),表征了最后一個(gè)HTTP重定向完成時(shí)(也就是說是HTTP響應(yīng)的最后一個(gè)比特直接被收到的時(shí)間)的UNIX時(shí)間戳。如果沒有重定向,或者重定向中的一個(gè)不同源,這個(gè)值會(huì)返回0.
let redirectEnd = timing.redirectEnd ;
//long 型的毫秒數(shù),表征了瀏覽器準(zhǔn)備好使用HTTP請(qǐng)求來獲取(fetch)文檔的UNIX時(shí)間戳。這個(gè)時(shí)間點(diǎn)會(huì)在檢查任何應(yīng)用緩存之前。
let fetchStart = timing.fetchStart ;
//long 型的毫秒數(shù),表征了域名查詢開始的UNIX時(shí)間戳。如果使用了持續(xù)連接(persistent connection),或者這個(gè)信息存儲(chǔ)到了緩存或者本地資源上,這個(gè)值將和fetchStart一致。
let domainLookupStart = timing.domainLookupStart ;
//long 型的毫秒數(shù),表征了域名查詢結(jié)束的UNIX時(shí)間戳。如果使用了持續(xù)連接(persistent connection),或者這個(gè)信息存儲(chǔ)到了緩存或者本地資源上,這個(gè)值將和 fetchStart一致。
let domainLookupEnd = timing.domainLookupEnd ;
//long 型的毫秒數(shù),返回HTTP請(qǐng)求開始向服務(wù)器發(fā)送時(shí)的Unix毫秒時(shí)間戳。如果使用持久連接(persistent connection),則返回值等同于fetchStart屬性的值。
let connectStart = timing.connectStart ;
//long 型的毫秒數(shù),返回瀏覽器與服務(wù)器之間的連接建立時(shí)的Unix毫秒時(shí)間戳。如果建立的是持久連接,則返回值等同于fetchStart屬性的值。連接建立指的是所有握手和認(rèn)證過程全部結(jié)束。
let connectEnd = timing.connectEnd ;
//long 型的毫秒數(shù),返回瀏覽器與服務(wù)器開始安全鏈接的握手時(shí)的Unix毫秒時(shí)間戳。如果當(dāng)前網(wǎng)頁不要求安全連接,則返回0
let secureConnectionStart = timing.secureConnectionStart ;
//long 型的毫秒數(shù),返回瀏覽器向服務(wù)器發(fā)出HTTP請(qǐng)求時(shí)(或開始讀取本地緩存時(shí))的Unix毫秒時(shí)間戳
let requestStart = timing.requestStart ;
//long 型的毫秒數(shù),返回瀏覽器從服務(wù)器收到(或從本地緩存讀取)第一個(gè)字節(jié)時(shí)的Unix毫秒時(shí)間戳。如果傳輸層在開始請(qǐng)求之后失敗并且連接被重開,該屬性將會(huì)被數(shù)制成新的請(qǐng)求的相對(duì)應(yīng)的發(fā)起時(shí)間。
let responseStart = timing.responseStart ;
//long 型的毫秒數(shù),返回瀏覽器從服務(wù)器收到(或從本地緩存讀取,或從本地資源讀取)最后一個(gè)字節(jié)時(shí)(如果在此之前HTTP連接已經(jīng)關(guān)閉,則返回關(guān)閉時(shí))的Unix毫秒時(shí)間戳。
let responseEnd = timing.responseEnd ;
//long 型的毫秒數(shù),返回當(dāng)前網(wǎng)頁DOM結(jié)構(gòu)開始解析時(shí)(即Document.readyState屬性變?yōu)椤發(fā)oading”、相應(yīng)的 readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳。
let domLoading = timing.domLoading ;
//long 型的毫秒數(shù),返回當(dāng)前網(wǎng)頁DOM結(jié)構(gòu)結(jié)束解析、開始加載內(nèi)嵌資源時(shí)(即Document.readyState屬性變?yōu)椤癷nteractive”、相應(yīng)的readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳
let domInteractive = timing.domInteractive ;
//ong 型的毫秒數(shù),返回當(dāng)解析器發(fā)送DOMContentLoaded 事件,即所有需要被執(zhí)行的腳本已經(jīng)被解析時(shí)的Unix毫秒時(shí)間戳
let domContentLoadedEventStart = timing.domContentLoadedEventStart ;
//long 型的毫秒數(shù),返回當(dāng)所有需要立即執(zhí)行的腳本已經(jīng)被執(zhí)行(不論執(zhí)行順序)時(shí)的Unix毫秒時(shí)間戳
let domContentLoadedEventEnd = timing.domContentLoadedEventEnd ;
//long 型的毫秒數(shù),返回當(dāng)前文檔解析完成,即Document.readyState 變?yōu)?'complete'且相對(duì)應(yīng)的readystatechange 被觸發(fā)時(shí)的Unix毫秒時(shí)間戳
let domComplete = timing.domComplete ;
//long 型的毫秒數(shù),返回該文檔下,load事件被發(fā)送時(shí)的Unix毫秒時(shí)間戳。如果這個(gè)事件還未被發(fā)送,它的值將會(huì)是0
let loadEventStart = timing.loadEventStart ;
//long 型的毫秒數(shù),返回當(dāng)load事件結(jié)束,即加載事件完成時(shí)的Unix毫秒時(shí)間戳。如果這個(gè)事件還未被發(fā)送,或者尚未完成,它的值將會(huì)是0
let loadEventEnd = timing.loadEventEnd ;
//呈現(xiàn)了如何導(dǎo)航到當(dāng)前文檔的信息
//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 = {};
//【重要】頁面加載完成的時(shí)間
//【原因】這幾乎代表了用戶等待頁面可用的時(shí)間
times.loadPage = t.loadEventEnd - t.navigationStart;
//【重要】解析 DOM 樹結(jié)構(gòu)的時(shí)間
//【原因】反省下你的 DOM 樹嵌套是不是太多了!
times.domReady = t.domComplete - t.responseEnd;
//【重要】重定向的時(shí)間
//【原因】拒絕重定向!比如,http://example.com/ 就不該寫成 http://example.com
times.redirect = t.redirectEnd - t.redirectStart;
//【重要】DNS 查詢時(shí)間
//【原因】DNS 預(yù)加載做了么?頁面內(nèi)是不是使用了太多不同的域名導(dǎo)致域名查詢的時(shí)間太長?
// 可使用 HTML5 Prefetch 預(yù)查詢 DNS ,見:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)
times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
//【重要】讀取頁面第一個(gè)字節(jié)的時(shí)間
//【原因】這可以理解為用戶拿到你的資源占用的時(shí)間,加異地機(jī)房了么,加CDN 處理了么?加帶寬了么?加 CPU 運(yùn)算速度了么?
// TTFB 即 Time To First Byte 的意思
// 維基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
times.ttfb = t.responseStart - t.navigationStart;
//【重要】內(nèi)容加載完成的時(shí)間
//【原因】頁面內(nèi)容經(jīng)過 gzip 壓縮了么,靜態(tài)資源 css/js 等壓縮了么?
times.request = t.responseEnd - t.requestStart;
//【重要】執(zhí)行 onload 回調(diào)函數(shù)的時(shí)間
//【原因】是否太多不必要的操作都放到 onload 回調(diào)函數(shù)里執(zhí)行了,考慮過延遲加載、按需加載的策略么?
times.loadEvent = t.loadEventEnd - t.loadEventStart;
// DNS 緩存時(shí)間
times.appcache = t.domainLookupStart - t.fetchStart;
// 卸載頁面的時(shí)間
times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;
// TCP 建立連接完成握手的時(shí)間
times.connect = t.connectEnd - t.connectStart;
return times;
}
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。