影響LCP的四個因素如下:
- 較慢的伺服器響應時間
- 渲染阻塞的js和css
- 較慢的資源載入時間
- 客戶端渲染
較慢的伺服器響應時間
瀏覽器獲取文件的時間越長,使用者看到頁面的時間也會越長。較快的伺服器響應時間,可以直接改善每一個頁面載入相關的指標,包括LCP。
可以使用 TTFB(Time to First Byte)
來測試伺服器響應時間,你可以通過以下手段來優化 TTFB
這個指標。
- 優化伺服器
- 使用CDN
- 快取資源
- html頁面優先使用快取
- 提前建立第三方連線
優化伺服器
你是否在伺服器上執行一些昂貴的查詢,佔用了伺服器一定的時間?或者伺服器上做了一些複雜的操作,導致頁面內容返回延後?分析和提升服務端程式碼的效率可以直接改善瀏覽器從服務端獲取資料的時間。
比起僅僅是提供靜態頁面而言,很多服務端的web框架都會動態建立頁面。換句話說,比起直接返回一個現存的html檔案,這些框架會執行一些邏輯來建立頁面。這就取決於資料庫查詢時間,或者UI框架建立元件的時間(比如: React)。大部分web框架都有提供效能指導,開發者可以根據指導來提升處理速度。
使用CDN
CDN全稱Content Delivery Network,是一個分佈在不同地域的伺服器叢集。如果瀏覽器去請求資料,CDN會選擇就近的一個伺服器給你返回,避免請求產生太長網路路徑,甚至跨國。
快取資源
如果你的html是靜態的,每一次請求都不會變,可以服務端快取下來,不用每次重新建立頁面。通過快取一份生成好的html到硬碟,服務端快取可以有效的降低TTFB,最小化資源的使用。
- 配置反向代理,在應用伺服器之前配置一個快取伺服器,專門提供快取內容。
- 配置和管理雲服務商提供的快取特性
- 使用CDN來提供內容快取,這樣使用者可以更近的獲取到內容
html頁面優先使用快取
service worker可以實現這樣一個功能,優先使用快取頁面,如果頁面有更新,會將新頁面快取下來,下次啟動則使用新快取。
下圖是使用service worker的一個對比:
提前建立第三方連線
發往第三方服務的請求也會影響LCP,特別是當頁面關鍵內容依賴它們的時候。使用 rel="preconnect"
告知瀏覽器你的資源連線需要儘可能地快。
<link rel="preconnect" href="https://example.com">
也可以使用 dns-prefetch
讓dns解析更快。
<link rel="dns-prefetch" href="https://example.com">
考慮到瀏覽器相容性, dns-prefetch
可以作為 preconnect
的一個fallback。
<head>
<link rel="preconnect" href="https://example.com">
<link rel="dns-prefetch" href="https://example.com">
</head>
preconnect一般只配置一個,dns-prefetch可以配置多個,所以一般把最關鍵的資源配置成preconnect,比如js或者css所在的cdn域名
渲染阻塞的js和css
在瀏覽器渲染任何內容之前,需要解析html,並生成dom樹。html解析器會被任何樣式檔案 <link rel="stylesheet">
以及同步指令碼 <script src="main.js">
阻塞並暫停解析。
這個不僅影響FCP,也影響了LCP。延遲載入非關鍵的js和css可以加速頁面的主內容的載入。
減少css阻塞時間
有以下三種方法:
- 最小化css
- 延後載入非關鍵的css
- 內聯關鍵css
最小化css
移除css中不必要的字元、註釋和空格等,一般打包工具都會自帶相應的外掛。
延後載入非關鍵的css
使用開發者工具中的 Coverage
可以找到頁面未使用的css。
優化方案:
- 移除無用的css,並將它們移到需要的頁面
- 用
rel="preload"
和onload
非同步載入對於初次渲染無用的css
內聯關鍵css
將關鍵路徑的css直接放在 <head>
中內聯引用:
這樣可以避免一次伺服器請求,讓css阻塞時間更短。
如果你無法手動新增內聯樣式,可以使用第三方庫:
- Critical, CriticalCSS, 和 Penthouse 可以提取並內聯css。
- Critters 是一個webpack外掛,可以幫你內聯關鍵css,其他css則懶載入。
減少js阻塞時間
有以下三種方案:
- 最小化並壓縮js檔案
- 延後未使用的js
- 儘量減少未使用的polyfills
減少資源載入時間
主要有以下幾種元素會影響LCP:
<img>
元素<svg>
中的<image>
元素<video>
元素(如果定義了封面圖,會影響LCP)- 帶
url()
背景圖的元素 - 塊級元素帶有文字節點或者內聯文字子元素
有以下幾種方式可以保證這些檔案儘快的載入:
- 優化和壓縮圖片
- 預載入重要資源
- 壓縮文字檔案
- 自適應服務,如:根據網路條件載入不同資源(例如:4g、3g、2g)
- 用service worker快取資源
優化和壓縮圖片
大部分網站的最大元素都是圖片,如果能加速這些圖片的載入,能有效改善LCP。參考以下步驟:
- 考慮是否必須使用圖片,如果不是必須,移除
- 壓縮圖片(用imagemin)
- 轉換成新格式(webp)
- 使用響應式圖片
- 使用圖片CDN
預載入重要資源
有時候,在css或者js中宣告或者使用的一個重要資源可能比你預期的要晚獲取,比如字型檔案。
如果你明確的知道某個資源需要提高優先順序,可以使用 <link rel="preload">
。
<link rel="preload" as="script" href="script.js">
<link rel="preload" as="style" href="style.css">
<link rel="preload" as="image" href="img.png">
<link rel="preload" as="video" href="vid.webm" type="video/webm">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
Chrome73版本之後,預載入同時支援響應式:
<link
rel="preload"
as="image"
href="wolf.jpg"
imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w"
imagesizes="50vw"
>
壓縮文字檔案
使用 Gzip
或者 Brotli
,gzip支援度較好,brotli的壓縮比更高,但只有更新的瀏覽器才支援。
自適應服務
當載入頁面的主內容的時候,可以根據使用者裝置和網路條件來載入不同的資源。要完成這些,可以使用 Network Information, Device Memory, 和 HardwareConcurrency 的api。
if (navigator.connection && navigator.connection.effectiveType) {
if (navigator.connection.effectiveType === '4g') {
// Load video
} else {
// Load image
}
}
以下是一些有用的屬性:
navigator.connection.effectiveType
: 網路型別,如4G等navigator.connection.saveData
: data-saver是否開啟navigator.hardwareConcurrency
: cpu核數navigator.deviceMemory
: 裝置記憶體
用service worker快取資源
可以直接使用 workbox 的第三方庫更方便的快取資源,這是谷歌提供的一個工具庫。
客戶端渲染
很多網站現在都使用客戶端渲染。一些框架像 angluar, react,vue等,可以方便的建立spa的單頁面應用,部分替代了伺服器的功能。
如果你構建的應用是以上這種方式,需要注意如果你的bundle的js檔案過大,會影響LCP。如果不做優化,使用者可能長時間無法看到頁面,也無法互動,直到js全部下載執行完。有以下幾個優化項:
- 減少關鍵js的大小
- 使用服務端渲染
- 使用預渲染
減少關鍵js的大小
與減少js阻塞時間一樣。
使用服務端渲染
這樣可以讓內容在服務端渲染好,然後返回給瀏覽器,以此來減少LCP。但這種方式存在一些隱患:
- 程式碼複雜性太高,服務端和客戶端用的是同一套js
- 服務端去執行js,並渲染html,會增加TTFB,相比於僅僅提供靜態html而言
- 服務端渲染的頁面看上去似乎可以互動,但實際上還是要等待js執行完成才能互動。簡單地說,會增加
TTI(Time to Interactive)
使用預渲染
預渲染是一個獨立的技術,主要是為了解決服務端渲染的程式碼複雜度。通過啟動一個無頭瀏覽器,在構建的時候就將每個路由頁面渲染好並建立對應的html檔案,這些檔案也可以和對應的js bundles一起使用。
預渲染的方式,不能改善TTI,但對TTFB會比服務端渲染要更好些。
開發者工具
開發者工具已經可以針對實驗資料進行這些指標的測試:
- lighthouse
- performance中的timeline
總結
本文只是對LCP的優化做了簡單而又比較全的介紹,後面的文章會針對個別項進行單獨的介紹。