深入理解白屏時間及優化策略
在前端性能優化中,白屏時間(First Paint Time)是一個非常重要的指標。指的是用戶輸入網址按下回車鍵,到瀏覽器開始渲染頁面內容的時間段。在這段時間內,用戶看到的只是一個空白頁面,因此白屏時間的長短影響了用戶的體驗。
什么是白屏時間
白屏時間指用戶發起頁面請求到瀏覽器首次開始渲染頁面內容的時間,白屏時間包含以下幾個階段:
1、DNS解析:瀏覽器將域名解析為IP地址
2、建立TCP連接:瀏覽器與服務器建立TCP連接(三次握手)
3、發送 HTTP 請求:瀏覽器向服務器發送HTTP請求
4、服務器響應:服務七處理請求并返回響應數據
5、瀏覽器解析HTML:瀏覽器解析HTML文檔并構建DOM樹
6、瀏覽器渲染頁面:瀏覽器根據DOM 樹和 CSS 樹生成渲染樹,開始渲染頁面
7、頁面展示第一個標簽:瀏覽器首次將頁面內容渲染到屏幕上
白屏時間的影響因素
1、網絡性能:網絡延遲、帶寬、DNS解析時間等都會影響白屏時間,如果網絡狀況不佳,DNS解析和TCP建立的時間會變長,從而導致白屏時間的增加
2、服務器性能:服務器的響應速度、處理能力也會影響白屏時間。如果服務器響應慢,瀏覽器需要等待更長時間才能接收到HTML文檔。
3、前端頁面結構:HTML文檔的大小、復雜度、外部資源的加載順序也都會影響白屏時間。如果HTML的文檔過大或包含大量的外部資源,瀏覽器需要更長時間來解析和渲染頁面
4、瀏覽器性能:瀏覽器的渲染引擎、緩存機制等也會影響白屏時間。不同的瀏覽器渲染性能可能存在差異,導致白屏時間不同。
如何測量白屏的時間
方法一:基于時間戳
在HTML文檔的 <head>標簽中插入js代碼記錄頁面開始加載的時間戳,然后再<head>標簽解析完成后,記錄另一個時間戳。兩者的差值為白屏時間
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>白屏時間計算</title>
<script>
// 記錄頁面開始加載的時間
window.pageStartTime = Date.now();
</script>
<link rel="stylesheet" >
<link rel="stylesheet" >
<script>
// head 解析完成后,記錄時間
window.firstPaint = Date.now();
console.log(`白屏時間:${firstPaint - pageStartTime}ms`);
</script>
</head>
<body>
<div class="container"></div>
</body>
</html>
方法二: 基于Performance API 的方法
使用 Performance API 可以精確的測量白屏時間。PerformanceAPI 提供了 PerformanceObserver接口,可以監聽頁面的首次繪制時間(first-paint)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" >
<link rel="stylesheet" >
<!-- 只是為了讓白屏時間更長一點 -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.js"></script>
</head>
<body>
<h1>Hello, World!</h1>
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_61ae954a0c4c41dba37b189a20423722@000000_oswg66502oswg900oswg600_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_9e1df42e783841e79ff021cda5fc6ed4@000000_oswg41322oswg1026oswg435_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_0376475b9a6a4dcab3f7b06a1b339cfc@5888275_oswg287301oswg729oswg545_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_e3213623ab5c46da8a6f9c339e1bd781@5888275_oswg1251766oswg1080oswg810_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_919d4445116f4efda326f651619b4c69@5888275_oswg169476oswg598oswg622_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<img
src="https://img.36krcdn.com/hsossms/20250217/v2_0457ccbedb984e2897c6d94815954aae@5888275_oswg383406oswg544oswg648_img_000?x-oss-process=image/format,jpg/interlace,1"
alt="">
<script>
// 性能 觀察器 觀察者模式
const observer = new PerformanceObserver((list) => {
// 獲取所有的 性能 指標
const entries = list.getEntries();
for(const entry of entries) {
// body 里的第一個 標簽的渲染
// 'first-paint' 表示頁面首次開始繪制的時間點,也就是白屏結束的時間點
if(entry.name === 'first-paint') {
const whiteScreenTime = entry.startTime;
console.log(`白屏時間:${whiteScreenTime}ms`);
}
}
})
// 首次繪制 first-paint
// 首次內容繪制 first-contentful-paint 事件
// observe 監聽性能指標
// buffered 屬性設置為 true,表示包含性能時間線緩沖區中已經記錄的相關事件
// 這樣即使在創建 PerformanceObserver 之前事件已經發生,也能被捕獲到
observer.observe({ type: 'paint', buffered: true });
</script>
</body>
</html>