如何加快回應使用者互動的速度。
我按了一下,但什麼都沒發生!為何我無法與這個網頁互動?糟糕
「首次顯示內容所需時間」(FCP) 和「最大內容繪製」(LCP) 都是評估網頁以視覺化方式轉譯 (繪製) 所需的時間。雖然載入時間很重要,但繪製時間並未擷取載入回應,也就是頁面回應使用者互動的速度。
首次輸入延遲時間 (FID) 是網站體驗核心指標指標,會擷取使用者對網站互動性和回應速度的第一印象。從使用者首次與網頁互動到瀏覽器實際回應操作所需的時間。FID 是欄位指標,無法在研究室環境中模擬。需要真實的使用者互動才能評估回應延遲時間。
為了在研究室中預測 FID,建議使用 Total Blocking Time (TBT)。這些方法會評估不同的內容,但 TBT 中的改善幅度通常對應於 FID 的改善項目。
FID 不佳的主因是大量的 JavaScript 執行。最佳化 JavaScript 剖析、編譯及執行網頁的方式,就能直接減少 FID。
大量 JavaScript 執行
在主執行緒上執行 JavaScript 時,瀏覽器無法回應大部分的使用者輸入資訊。換句話說,瀏覽器無法在主執行緒忙碌時回應使用者互動。改善建議:
分解長時間工作
如果您已嘗試減少在單一網頁上載入的 JavaScript 數量,那麼將長時間執行的程式碼分成「較小的非同步工作」可能會很有幫助。
「長時間工作」是 JavaScript 執行期間,使用者可能會發現 UI 沒有回應。而封鎖主執行緒超過 50 毫秒的任何程式碼,都可被歸類為「長工作」。長時間工作是潛在的 JavaScript 負荷 (使用者目前可能難以載入並執行)。分割冗長的工作可以縮短網站的輸入延遲時間。
在您採用程式碼分割和分割長時間工作等最佳做法時,FID 應該會明顯改善。雖然 TBT 不是欄位指標,但在最終改善互動時間 (TTI) 和 FID 方面,這項做法非常實用。
將網頁調整至最佳互動準備
以下是網路應用程式中主要仰賴 JavaScript 導致 FID 和 TBT 分數不佳的常見原因:
執行第一方指令碼可能會延遲互動準備
- JavaScript 大小過大、執行時間過長和區塊化缺乏效率,可能會拖慢網頁回應使用者輸入內容的速度,並影響 FID、TBT 和 TTI。漸進式載入程式碼和功能,有助於分散工作並改善互動準備度。
- 伺服器端算繪的應用程式看起來可能會快速繪製像素,但請留意大型指令碼執行作業會妨礙使用者互動(例如透過重水以連接事件監聽器)。如果您使用路徑式程式碼分割功能,則可能需要幾百毫秒,有時甚至數秒。請考慮在建構期間轉移更多伺服器端邏輯,或以靜態方式產生更多內容。
以下是改進應用程式的第一方指令碼載入前後的 TBT 分數。將重要路徑中非必要元件的指令碼載入 (並執行) 移到其他位置,使用者就能更快與網頁互動。
資料擷取會影響互動準備的許多層面
- 等待串聯擷取刊登序列 (例如 JavaScript 和資料擷取) 可能會影響互動延遲時間。盡量降低對階層式資料擷取的依賴。
- 大型內嵌資料儲存庫可能會增加 HTML 剖析時間,並同時影響繪製和互動指標。請盡量降低在用戶端後續處理的資料量。
第三方指令碼執行可能會延遲互動延遲時間
- 許多網站都含有第三方代碼和分析,能讓網路保持忙碌狀態,並讓主執行緒定期沒有回應,進而影響互動延遲。探索第三方程式碼的隨選載入功能 (舉例來說,您可能不會載入這些需捲動位置的廣告,直到將這些廣告捲動至可視區域附近為止)。
- 在某些情況下,第三方指令碼可能會根據主要執行緒的優先順序和頻寬來先佔第一方指令碼,也會延遲網頁準備好互動的時間。因此,建議您優先載入您認為對使用者最有價值的載入內容。
使用網路工作站
主要執行緒遭到封鎖,是導致輸入延遲的主因之一。網路工作站可讓您在背景執行緒上執行 JavaScript。將非 UI 作業移至個別背景工作執行緒可能會縮短主執行緒的封鎖時間,進而改善 FID。
建議您使用下列程式庫,讓您的網站更容易使用網路工作程式:
縮短 JavaScript 執行時間
限制網頁上的 JavaScript 數量,可以減少瀏覽器所需執行 JavaScript 程式碼的時間。這會加快瀏覽器回應任何使用者互動的速度。
如何減少網頁執行的 JavaScript:
- 延後未使用的 JavaScript
- 盡量減少未使用的 polyfill
延後未使用的 JavaScript
根據預設,所有的 JavaScript 都會禁止轉譯。瀏覽器遇到連結至外部 JavaScript 檔案的指令碼標記時,必須暫停目前的程式碼,並下載、剖析、編譯及執行該 JavaScript。因此,請只載入頁面或回應使用者輸入內容所需的程式碼。
Chrome 開發人員工具的「涵蓋範圍」分頁會顯示網頁未使用 JavaScript 的程度。
如何減少未使用的 JavaScript:
- 將套件分割成多個區塊
- 使用
async
或defer
延遲任何非關鍵 JavaScript,包括第三方指令碼。
程式碼分割是將一個大型 JavaScript 組合分割成較小的區塊,這些區塊可以有條件載入 (也稱為延遲載入)。大多數新版瀏覽器都支援動態匯入語法,允許隨選擷取模組:
import('module.js').then((module) => {
// Do something with the module.
});
針對特定使用者互動 (例如變更路徑或顯示互動視窗) 動態匯入 JavaScript,可確保只在必要時擷取不會用於初始頁面載入的程式碼。
除了一般瀏覽器支援外,動態匯入語法也可以用於許多不同的建構系統中。
- 如果您使用 webpack、Rollup 或 Parcel 做為模組套裝組合,請善用其動態匯入支援功能。
- React、Angular 和 Vue 等用戶端架構提供了抽象化,讓您能夠更輕鬆地在元件層級延遲載入。
除了程式碼分割之外,請一律針對重要路徑或不需捲動位置內容不需要的指令碼使用非同步或延遲模式。
<script defer src="…"></script>
<script async src="…"></script>
除非有特殊原因,否則所有第三方指令碼都應根據預設使用 defer
或 async
載入。
盡量減少未使用的 polyfill
如果您使用新型 JavaScript 語法編寫程式碼並參照新型瀏覽器 API,則必須將其轉譯並納入 polyfill,才能在舊版瀏覽器中運作。
在網站上加入 polyfill 和轉碼程式碼的主要效能問題之一,就是新版瀏覽器如果不需要更新,就不需要下載此程式碼。如要縮減應用程式的 JavaScript 大小,請盡可能減少未使用的 polyfill,並限制其用於需要的環境。
如要針對網站使用 polyfill 進行最佳化作業:
- 如果您使用 Babel 做為轉譯器,請使用
@babel/preset-env
,僅針對預計指定瀏覽器的瀏覽器加入所需的 polyfill。若是 Babel 7.9,啟用bugfixes
選項即可進一步減少任何不需要的 polyfill 使用模組/無模組模式提供兩個不同的套件 (
@babel/preset-env
也支援透過target.esmodules
支援)<script type="module" src="modern.js"></script> <script nomodule src="legacy.js" defer></script>
支援 JavaScript 模組的環境已支援許多使用 Babel 編譯的較新 ECMAScript 功能。如此一來,您就可以簡化整個程序,確保只使用經過轉譯的程式碼,只用於確實有需要的瀏覽器。
開發人員工具
下列工具可用於評估 FID 及進行偵錯:
Lighthouse 6.0 是欄位指標,因此不含 FID。不過,Total Blocking Time (TBT) 可當做 Proxy 使用。改善 TBT 的最佳化作業也應改善欄位中的 FID。
Chrome 使用者體驗報告提供從來源層級匯總的實際 FID 值
感謝 Philip Walton、Kayce Basques、Ilya Grigorik 和 Annie Sullivan 的評論。