iT邦幫忙

2025 iThome 鐵人賽

DAY 21
2
Modern Web

從 Canvas 到各式各樣的 Web API 之旅系列 第 21

Day 21 - 量化網頁速度!用 Performance API 看載入、繪製、資源的效能表現

  • 分享至 

  • xImage
  •  

寫到這裡,我們已經認識了不少 Web API:從 Canvas 到各種裝置、分享、鎖定、互動,這些功能大多「能做什麼」。今天換個角度,談談「做得怎麼樣」——Performance API。 🧮 ⏱️


為什麼需要 Performance API?

當網頁變得越來越複雜、動態,效能(Performance)不再只是感覺上的「快」或「慢」。我們需要更精確的數據,來回答:

  • 這段程式跑了多久?
  • 頁面載入到底花多少時間?
  • 使用者點擊按鈕後,多久才看到回饋?

Performance API 就是瀏覽器提供的一套工具,讓我們能量化這些問題,而不是憑感覺。


performance.now()

最常見、最基礎的方法:

const start = performance.now();

for (let i = 0; i < 1e6; i++) {
  // 做一些計算
}

const end = performance.now();
console.log(`花費時間: ${(end - start).toFixed(2)} ms`);

比起 Date.now(),它的解析度更高(小數毫秒),也不會因為系統時間被調整而受影響。


Performance Timeline

瀏覽器會把「載入階段」、「首次繪製」與「資源下載」的時間軸以 Performance Timeline 暴露出來。

// 1) 導覽/載入階段
const nav = performance.getEntriesByType("navigation")[0];
console.table(nav);

// 2) 首次繪製
console.table(performance.getEntriesByType("paint"));

// 3) 資源載入
console.table(performance.getEntriesByType("resource"));

1. 導覽(navigation)重點欄位

這些欄位來自 PerformanceNavigationTiming,屬於 Navigation Timing Level 2。

  • startTime:此 entry 的起點,對 navigation 一律為 0
  • fetchStart / domainLookupStart/End / connectStart/End / secureConnectionStart:網路階段細節(DNS、連線、TLS)。
  • requestStart / responseStart / responseEnd:要求送出、回應開始、回應結束。
  • domInteractive:DOM 完成解析;JS 可以安全地操作 DOM。
  • domContentLoadedEventStart/EndDOMContentLoaded 事件時間。
  • domComplete:DOM 與資源載入告一段落。
  • loadEventStart / loadEventEndload 事件起訖。
  • typenavigate / reload / back_forward
  • transferSize / encodedBodySize / decodedBodySize:傳輸大小、壓縮前/後大小。

2. 首次繪製(paint)重點欄位

performance.getEntriesByType('paint') 會回傳 PerformancePaintTiming 陣列:

  • first-paint (FP):第一次任何像素被繪製的時間(有些瀏覽器可能不提供)。
  • first-contentful-paint (FCP):第一次有文字/圖片等內容出現在畫面。

3. 資源(resource)重點欄位

每個靜態資源皆為 PerformanceResourceTiming

  • name:資源 URL。
  • initiatorTypeimgscriptlink
  • startTime / duration:開始時間與總耗時(毫秒)。
  • transferSize / encodedBodySize / decodedBodySize:傳輸大小、壓縮前/後大小。

4. 最大內容繪製(LCP)

LCP(Largest Contentful Paint)衡量「視窗中最大的內容」完成繪製的時間,是 Core Web Vitals 的核心指標之一。它不會出現在 navigation / paint / resource 清單,必須用 PerformanceObserver 監聽。

取得 LCP 的正確方式

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // entry 為 LargestContentfulPaint
    console.log('LCP candidate:', entry.startTime, entry.size, entry);
  }
});
po.observe({ type: 'largest-contentful-paint', buffered: true });

// 建議在首次互動(如 click / keydown)後停止觀察,凍結最終 LCP:
addEventListener('pointerdown', () => po.disconnect(), { once: true });
addEventListener('keydown', () => po.disconnect(), { once: true });

LCP 補充說明

  • LCP 候選值可能因圖片載入、字體、內容變動而更新;使用 buffered: true 可補抓頁面早期的紀錄。
  • 當頁面背景化(tab 切走)或使用者互動後,瀏覽器會決定最終 LCP 值;上方程式碼在首次互動後 disconnect(),可拿到更穩定的最終值。

標記與量測 (Marks & Measures)

Performance API 也允許開發者自訂「錨點」:

performance.mark("start-task");

// 執行某個任務

performance.mark("end-task");
performance.measure("task-duration", "start-task", "end-task");

console.log(performance.getEntriesByType("measure"));

回傳值重點

performance.getEntriesByType("measure") 回傳陣列,元素為 PerformanceMeasure 物件,包含:

  • name:這段量測的名稱(例:task-duration)。
  • entryType:固定為 measure
  • startTime:起始標記的時間點。
  • duration:耗時(end - start,毫秒)。

例如:

[
  {
    "name": "task-duration",
    "entryType": "measure",
    "startTime": 123.4,
    "duration": 156.7
  }
]

這讓我們可以很精確地追蹤「特定任務」到底花多久。


實務應用

  • 偵測瓶頸:找出是 API 回應慢,還是 DOM 操作慢。
  • 前端監控(RUM):搭配日誌系統,把量測數據上報。
  • 優化迭代:調整 Lazy Load、Cache、壓縮,觀察數據是否改善。

總結

Performance API 的核心價值在於「量化」:

  • performance.now():高解析度時間量測。
  • Timeline:自動收集載入、資源、繪製時間(含 navigation、paint、Resource)。
  • LCP:以 PerformanceObserver 監聽的關鍵體驗指標。
  • Marks & Measures:自訂錨點,追蹤流程效能。

有了這些數據,效能優化就不再是憑直覺,而是有數據、有依據的工程。🚀


👉 歡迎追蹤這個系列,我會從 Canvas 開始,一步步帶你認識更多 Web API 🎯


上一篇
Day 20 - Web Animations API 原理:為什麼把動畫交給 compositor 更滑順?撲克牌堆疊、收合、洗牌示範
下一篇
Day 22 - IndexedDB 初探:瀏覽器裡的資料庫
系列文
從 Canvas 到各式各樣的 Web API 之旅22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言