用戶端轉譯 HTML 和互動功能

使用 JavaScript 轉譯 HTML 與伺服器傳送的 HTML 轉譯方式不同,而且可能會影響效能。瞭解本指南中的差異,以及如何維持網站的算繪效能,尤其是在互動方面。

對於使用瀏覽器內建導覽邏輯的網站 (有時稱為「傳統網頁載入」或「硬性導覽」),瀏覽器預設會非常妥善地剖析及算繪 HTML。這類網站有時稱為多頁面應用程式 (MPA)。

不過,開發人員可以根據應用程式需求,調整瀏覽器的預設值。使用單頁面應用程式 (SPA) 模式的網站就是這種情況,因為這類網站會使用 JavaScript 在用戶端上動態建立大部分的 HTML/DOM。這個設計模式稱為「用戶端算繪」,如果涉及的工作過多,可能會影響網站的 Interaction to Next Paint (INP)

這份指南將協助您權衡使用伺服器傳送至瀏覽器的 HTML,與使用 JavaScript 在用戶端建立 HTML 的差異,以及後者如何在關鍵時刻造成高互動延遲。

瀏覽器如何轉譯伺服器提供的 HTML

傳統網頁載入作業中使用的導覽模式,會在每次導覽時從伺服器接收 HTML。如果您在瀏覽器的網址列中輸入網址,或點選多媒體廣告中的連結,系統就會發生以下一系列事件:

  1. 瀏覽器會針對提供的網址傳送導覽要求。
  2. 伺服器會以分段 HTML 回應。

其中最後一個步驟非常重要。這也是伺服器/瀏覽器交換中最基本的效能最佳化方式,稱為「串流」。如果伺服器可以盡快開始傳送 HTML,且瀏覽器不必等待整個回應到達,則瀏覽器可以逐一處理傳送的 HTML。

擷取畫面:在 Chrome 開發人員工具的效能面板中,呈現伺服器傳送的 HTML 剖析結果。隨著 HTML 流入,系統會透過多項較短的工作處理 HTML 區塊,並逐漸完成轉譯。
剖析及算繪伺服器提供的 HTML,並在 Chrome 開發人員工具的效能面板中以視覺化方式呈現。剖析及轉譯 HTML 的相關工作會分成多個區塊。

與瀏覽器中發生的大多數事件一樣,HTML 剖析作業會在工作中執行。當 HTML 從伺服器串流至瀏覽器時,瀏覽器會逐漸剖析該 HTML,以便在該串流的位元到達時,進行最佳化剖析。結果是,瀏覽器會在處理每個區塊後定期交出主執行緒,以避免長時間工作。這表示在剖析 HTML 時,系統可以執行其他工作,包括向使用者呈現網頁所需的增量轉譯工作,以及處理網頁在重要啟動期間可能發生的使用者互動。這種做法可讓網頁的 Interaction to Next Paint (INP) 分數提升。

結論是什麼?從伺服器串流傳送 HTML 時,您可以免費取得 HTML 的逐步剖析和算繪功能,並自動產生至主執行緒。但用戶端轉譯無法提供這項功能。

瀏覽器如何轉譯 JavaScript 提供的 HTML

雖然每個網頁導覽要求都需要伺服器提供一些 HTML,但部分網站會使用 SPA 模式。這種做法通常會涉及伺服器提供的 HTML 初始酬載,但用戶端會使用從伺服器擷取的資料組合 HTML,填入網頁的主要內容區域。後續導覽 (在本例中稱為「軟性導覽」) 完全由 JavaScript 處理,以便在網頁中填入新的 HTML。

在某些較為受限的情況下,非 SPA 也可能會發生用戶端轉譯,例如透過 JavaScript 將 HTML 動態新增至 DOM。

透過 JavaScript 建立 HTML 或新增至 DOM 的方法有幾種:

  1. innerHTML 屬性可讓您透過字串設定現有元素的內容,瀏覽器會將該字串剖析成 DOM。
  2. document.createElement 方法可讓您建立要新增至 DOM 的新元素,而無需使用任何瀏覽器 HTML 剖析。
  3. document.write 方法可讓您將 HTML 寫入文件 (瀏覽器會剖析該文件,就像方法 1 一樣)。不過,基於多種原因強烈建議您不要使用 document.write
透過 JavaScript 算繪的 HTML 剖析螢幕截圖,可在 Chrome 開發人員工具的「Performance」面板中查看。工作會在單一長時間工作中發生,而該工作會封鎖主執行緒。
在用戶端透過 JavaScript 剖析及轉譯 HTML,如 Chrome 開發人員工具的效能面板所示。解析和轉譯作業並未分割,導致長時間的工作會封鎖主執行緒。

透過用戶端 JavaScript 建立 HTML/DOM 可能會造成重大後果:

  • 與伺服器在回應導覽要求時串流傳輸的 HTML 不同,用戶端上的 JavaScript 工作不會自動分割,因此可能會導致長時間的工作封鎖主執行緒。也就是說,如果您在用戶端上一次建立太多 HTML/DOM,網頁的 INP 可能會受到負面影響。
  • 如果在啟動期間在用戶端建立 HTML,瀏覽器預先載入掃描器不會偵測其中的資源。這肯定會對網頁的最大內容繪製 (LCP) 造成負面影響。雖然這不是執行階段效能問題 (而是擷取重要資源時的網路延遲問題),但您不希望網站的 LCP 受到忽略這項基本瀏覽器效能最佳化功能的影響。

如何因應用戶端算繪對效能造成的影響

如果您的網站高度仰賴用戶端端算繪製,且您發現欄位資料中的 INP 值不佳,可能會想知道用戶端端算繪製是否與問題有關。舉例來說,如果您的網站是 SPA,欄位資料可能會揭露造成大量轉譯工作的互動。

無論原因為何,以下是一些可能的原因,你可以嘗試找出原因,並解決問題。

盡可能提供來自伺服器的 HTML

如先前所述,瀏覽器預設會以高效能的方式處理伺服器傳來的 HTML。它會以避免長時間工作的方式分割 HTML 剖析和轉譯作業,並最佳化主執行緒的總時間。這會導致總封鎖時間 (TBT) 降低,而 TBT 與 INP 有密切關聯

您可能會使用前端架構來建構網站。如果是這樣,請務必在伺服器上轉譯元件 HTML。這樣一來,網站就不需要進行太多初始用戶端轉譯作業,使用者體驗也應會有所提升。

  • 針對 React,您需要使用 Server DOM API 在伺服器上轉譯 HTML。但請注意:傳統的伺服器端轉譯方法採用同步方法,可能會導致首次輸出位元組時間 (TTFB) 和後續指標 (例如 首次顯示內容所需時間 (FCP) 和 LCP) 變長。請盡可能使用 Node.js其他 JavaScript 執行階段的串流 API,以便伺服器盡快開始將 HTML 串流傳送至瀏覽器。Next.js 是基於 React 的架構,預設提供許多最佳做法。除了在伺服器上自動轉譯 HTML,還可為不會根據使用者情境 (例如驗證) 而變更的網頁,以靜態方式產生 HTML。
  • Vue 也會根據預設執行用戶端轉譯。不過,與 React 一樣,Vue 也可以在伺服器上轉譯元件 HTML。請盡可能善用這些伺服器端 API,或是考慮為 Vue 專案使用較高層級的抽象化,以便更輕鬆地實作最佳做法。
  • 根據預設,Svelte 會在伺服器上轉譯 HTML,但如果元件程式碼需要存取瀏覽器專屬的命名空間 (例如 window),您可能無法在伺服器上轉譯該元件的 HTML。盡可能探索其他方法,避免造成不必要的用戶端轉譯。SvelteKit 是 Svelte 的 Next.js,會盡可能在 Svelte 專案中嵌入許多最佳做法,讓您避免單獨使用 Svelte 的專案中潛在的陷阱。

限制在用戶端建立的 DOM 節點數量

DOM 越大,轉譯所需的處理作業就會增加。無論您的網站是完整的 SPA,還是會在 MPA 互動後將新節點插入現有 DOM,請盡可能縮小這些 DOM。這有助於減少在用戶端算繪期間顯示 HTML 所需的工作量,進而降低網站的 INP。

考慮使用串流服務 worker 架構

這是一種進階技巧,可能無法輕易套用於所有用途,但可將 MPA 轉換為讓使用者在瀏覽網頁時有立即載入的感覺。您可以使用 Service Worker 在 CacheStorage 中預先快取網站的靜態部分,同時使用 ReadableStream API 從伺服器擷取網頁的 HTML 其餘部分。

成功使用這項技術後,您就不會在用戶端上建立 HTML,但從快取中即時載入內容部分,會讓使用者覺得網站載入速度很快。採用這種做法的網站幾乎可視為 SPA,但不會有用戶端轉譯的缺點。這也減少了您向伺服器要求的 HTML 數量

簡而言之,串流服務 worker 架構並不會取代瀏覽器內建的導覽邏輯,而是新增導覽邏輯。如要進一步瞭解如何透過 Workbox 達成這項目標,請參閱「透過串流加快多頁應用程式」。

結論

網站接收及轉譯 HTML 的方式會影響效能。如果您依賴伺服器傳送網站運作所需的所有 (或大部分) HTML,就能免費獲得許多好處:逐步剖析和轉譯,以及自動產生主執行緒,以避免長時間的工作。

用戶端 HTML 算繪會引發許多潛在效能問題,但在許多情況下,這些問題是可以避免的。不過,由於每個網站的需求不同,因此無法 100% 避免這類情況。為減少因過度在用戶端進行轉譯而導致的長時間工作,請盡可能從伺服器傳送網站的 HTML,並盡可能縮小必須在用戶端轉譯的 HTML 的 DOM 大小。此外,請考慮採用其他架構,以便加快將 HTML 傳送至用戶端的速度,同時善用瀏覽器為從伺服器載入的 HTML 提供的逐步剖析和轉譯功能。

如果您能盡可能減少網站的用戶端算繪作業,不僅能改善網站的 INP,還能改善其他指標,例如 LCP、TBT,甚至在某些情況下還能改善 TTFB。

主頁橫幅圖片取自 Unsplash,圖片由 Maik Jonietz 提供。