Performance
一、簡介
Web 性能是客觀的衡量標(biāo)準(zhǔn),是用戶對加載時間和運行時的直觀體驗。Web 性能指頁面加載到可交互和可響應(yīng)所消耗的時間,以及頁面在交互時的流暢度——滾動是否順滑?按鈕能否點擊?彈窗能否快速打開,動畫是否平滑?Web 性能既包括客觀的度量如加載時間,每秒幀數(shù)和到頁面可交互的時間;也包括用戶的對頁面內(nèi)容加載時間的主觀感覺。
二、關(guān)鍵性能指南
-
CSS and JavaScript animation performance
Browsers are able to optimize rendering flows. In summary, we should always try to create our animations using CSS transitions/animations where possible. If your animations are really complex, you may have to rely on JavaScript-based animations instead.
dns-prefetch
當(dāng)瀏覽器從第三方服務(wù)跨域請求資源的時候,在瀏覽器發(fā)起請求之前,這個第三方的跨域域名需要被解析為一個 IP 地址,這個過程就是 DNS 解析,DNS 緩存可以用來減少這個過程的耗時,DNS 解析可能會增加請求的延遲,對于那些需要請求許多第三方的資源的網(wǎng)站而言,DNS 解析的耗時延遲可能會大大降低網(wǎng)頁加載性能。優(yōu)化啟動性能
不論在什么平臺上,盡可能快地啟動總是一個好主意。因為這是個很寬泛的問題,在這里我們不會著重關(guān)注。相反我們會關(guān)注構(gòu)建 Web 應(yīng)用時更重要的一個問題:盡可能異步地啟動。這意味著不要將你所有的啟動代碼在應(yīng)用主線程中的唯一一個事件處理函數(shù)中運行。關(guān)鍵渲染路徑
關(guān)鍵渲染路徑是指瀏覽器通過把 HTML、CSS 和 JavaScript 轉(zhuǎn)化成屏幕上的像素的步驟順序。優(yōu)化關(guān)鍵渲染路徑可以提高渲染性能。關(guān)鍵渲染路徑包含了 Document Object Model (DOM),CSS Object Model (CSSOM),渲染樹和布局。
三、 使用 Performance API
Performance API
Performance 接口可以獲取到當(dāng)前頁面中與性能相關(guān)的信息。它是 High Resolution Time API 的一部分,同時也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。該類型的對象可以通過調(diào)用只讀屬性 Window.performance 來獲得。
屬性
- Performance.navigation
PerformanceNavigation 對象提供了在指定的時間段里發(fā)生的操作相關(guān)信息,包括頁面是加載還是刷新、發(fā)生了多少次重定向等等.
包含屬性:
type:表示是如何導(dǎo)航到這個頁面的
1:點擊刷新頁面按鈕或者通過Location.reload()方法顯示的頁面
0:當(dāng)前頁面是通過點擊鏈接,書簽和表單提交,或者腳本操作,或者在url中直接輸入地址
2:頁面通過歷史記錄和前進后退訪問時
255:任何其他方式
redirectCount:表示在到達這個頁面之前重定向了多少次
- Performance.timing
PerformanceTiming 對象包含延遲相關(guān)的性能信息
包含屬性:
navigationStart:從同一個瀏覽器上下文的上一個文檔卸載(unload)結(jié)束時的UNIX時間戳。如果沒有上一個文檔,這個值會和PerformanceTiming.fetchStart相同;
unloadEventStart:unload事件拋出時的UNIX時間戳。如果沒有上一個文檔這個值會返回0
redirectStart:第一個HTTP重定向開始時的UNIX時間戳。如果沒有重定向,或者重定向中的一個不同源,這個值會返回0.
redirectEnd :最后一個HTTP重定向完成時(也就是說是HTTP響應(yīng)的最后一個比特直接被收到的時間)的UNIX時間戳。如果沒有重定向,或者重定向中的一個不同源,這個值會返回0;
fetchStart:瀏覽器準(zhǔn)備好使用HTTP請求來獲取(fetch)文檔的UNIX時間戳。這個時間點會在檢查任何應(yīng)用緩存之前;
domainLookupStart:表征了域名查詢開始的UNIX時間戳。如果使用了持續(xù)連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和 PerformanceTiming.fetchStart一致;
domainLookupEnd:表征了域名查詢結(jié)束的UNIX時間戳。如果使用了持續(xù)連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和 PerformanceTiming.fetchStart一致
connectStart:返回HTTP請求開始向服務(wù)器發(fā)送時的Unix毫秒時間戳。如果使用持久連接(persistent connection),則返回值等同于fetchStart屬性的值。
更多屬性查閱:https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceTiming
- performance.memory
Chrome 添加的一個非標(biāo)準(zhǔn)擴展,這個屬性提供了一個可以獲取到基本內(nèi)存使用情況的對象
包含屬性:
jsHeapSizeLimit:The maximum size of the heap, in bytes, that is available to the context.
totalJSHeapSize:The total allocated heap size, in bytes.
usedJSHeapSize:The currently active segment of JS heap, in bytes.
- Performance.timeOrigin
返回一個表示 the performance measurement 開始時間的高精度 timestamp
方法
- Performance.clearMarks()
將給定的 mark 從瀏覽器的性能輸入緩沖區(qū)中移除。
- Performance.clearMeasures()
將給定的 measure 從瀏覽器的性能輸入緩沖區(qū)中移除。
- Performance.clearResourceTimings()
從瀏覽器的性能數(shù)據(jù)緩沖區(qū)中移除所有 entryType 是 "resource" 的 performance entries。
- Performance.getEntries()
基于給定的 filter 返回一個 PerformanceEntry 對象的列表。
- Performance.getEntriesByName()
基于給定的 name 和 entry type 返回一個 PerformanceEntry 對象的列表。
- Performance.getEntriesByType()
基于給定的 entry type 返回一個 PerformanceEntry 對象的列表
- Performance.mark()
根據(jù)給出 name 值,在瀏覽器的性能輸入緩沖區(qū)中創(chuàng)建一個相關(guān)的 timestamp
- Performance.measure()
在瀏覽器的指定 start mark 和 end mark 間的性能輸入緩沖區(qū)中創(chuàng)建一個指定的 timestamp
- Performance.now()
返回一個表示從性能測量時刻開始經(jīng)過的毫秒數(shù) DOMHighResTimeStamp
- Performance.setResourceTimingBufferSize()
將瀏覽器的資源 timing 緩沖區(qū)的大小設(shè)置為 "resource" type performance entry 對象的指定數(shù)量
- Performance.toJSON()
其是一個 JSON 格式轉(zhuǎn)化器,返回 Performance 對象的 JSON 對象
使用例子
- 計算資源加載各階段的時間
重定向 (redirectStart 和 redirectEnd ),DNS 查詢(domainLookupStart 和 domainLookupEnd),TCP 握手 (connectStart 和 connectEnd), 響應(yīng) (responseStart 和 responseEnd)。 這段例子也計算了從開始獲取資源和請求開始(分別為 fetchStart and requestStart)到響應(yīng)結(jié)束 (responseEnd) 的時間
function calculate_load_times() {
// Check performance support
if (performance === undefined) {
console.log("= Calculate Load Times: performance NOT supported");
return;
}
// Get a list of "resource" performance entries
var resources = performance.getEntriesByType("resource");
if (resources === undefined || resources.length <= 0) {
console.log("= Calculate Load Times: there are NO `resource` performance records");
return;
}
console.log("= Calculate Load Times");
for (var i=0; i < resources.length; i++) {
console.log("== Resource[" + i + "] - " + resources[i].name);
// Redirect time
var t = resources[i].redirectEnd - resources[i].redirectStart;
console.log("... Redirect time = " + t);
// DNS time
t = resources[i].domainLookupEnd - resources[i].domainLookupStart;
console.log("... DNS lookup time = " + t);
// TCP handshake time
t = resources[i].connectEnd - resources[i].connectStart;
console.log("... TCP time = " + t);
// Secure connection time
t = (resources[i].secureConnectionStart > 0) ? (resources[i].connectEnd - resources[i].secureConnectionStart) : "0";
console.log("... Secure connection time = " + t);
// Response time
t = resources[i].responseEnd - resources[i].responseStart;
console.log("... Response time = " + t);
// Fetch until response end
t = (resources[i].fetchStart > 0) ? (resources[i].responseEnd - resources[i].fetchStart) : "0";
console.log("... Fetch until response end time = " + t);
// Request start until reponse end
t = (resources[i].requestStart > 0) ? (resources[i].responseEnd - resources[i].requestStart) : "0";
console.log("... Request start until response end time = " + t);
// Start until reponse end
t = (resources[i].startTime > 0) ? (resources[i].responseEnd - resources[i].startTime) : "0";
console.log("... Start until response end time = " + t);
}
}
- 計算資源大小
function display_size_data(){
// Check for support of the PerformanceResourceTiming.*size properties and print their values
// if supported.
if (performance === undefined) {
console.log("= Display Size Data: performance NOT supported");
return;
}
var list = performance.getEntriesByType("resource");
if (list === undefined) {
console.log("= Display Size Data: performance.getEntriesByType() is NOT supported");
return;
}
// For each "resource", display its *Size property values
console.log("= Display Size Data");
for (var i=0; i < list.length; i++) {
console.log("== Resource[" + i + "] - " + list[i].name);
if ("decodedBodySize" in list[i])
console.log("... decodedBodySize[" + i + "] = " + list[i].decodedBodySize);
else
console.log("... decodedBodySize[" + i + "] = NOT supported");
if ("encodedBodySize" in list[i])
console.log("... encodedBodySize[" + i + "] = " + list[i].encodedBodySize);
else
console.log("... encodedBodySize[" + i + "] = NOT supported");
if ("transferSize" in list[i])
console.log("... transferSize[" + i + "] = " + list[i].transferSize);
else
console.log("... transferSize[" + i + "] = NOT supported");
}
}
- 測量執(zhí)行性能
// 標(biāo)記一個開始點
performance.mark("mySetTimeout-start");
// 等待1000ms
setTimeout(function() {
// 標(biāo)記一個結(jié)束點
performance.mark("mySetTimeout-end");
// 標(biāo)記開始點和結(jié)束點之間的時間戳
performance.measure(
"mySetTimeout",
"mySetTimeout-start",
"mySetTimeout-end"
);
// 獲取所有名稱為mySetTimeout的measures
var measures = performance.getEntriesByName("mySetTimeout");
var measure = measures[0];
console.log("setTimeout milliseconds:", measure.duration)
// 清除標(biāo)記
performance.clearMarks();
performance.clearMeasures();
}, 1000);