最佳化 Next.js 中的第三方指令碼載入

瞭解 Next.js 指令碼元件背後的願景,其中提供的內建解決方案可將第三方指令碼載入最佳化。

Leena Sohoni
Leena Sohoni

在行動裝置和電腦上放送的網站要求中,約有 45% 是第三方提出的要求,其中 33% 是指令碼。第三方指令碼的大小、延遲時間和載入情形,可能會大幅影響網站的效能。Next.js 指令碼元件內建最佳做法和預設值,可協助開發人員在應用程式中導入第三方指令碼,同時處理立即可用的效能問題。

第三方指令碼及其對成效的影響

第三方指令碼可讓網頁開發人員運用現有解決方案導入常用功能,並縮短開發時間。不過,編寫這些指令碼的建立者通常沒有獎勵,考量到這類網站對瀏覽網站的成效有何影響。這些指令碼也是開發人員使用這些指令碼時的黑邊。

指令碼可容納大量由網站下載的第三方位元組,並涵蓋不同類別的第三方要求。根據預設,瀏覽器會根據指令碼在文件中的位置決定指令碼的優先順序,因此系統可能會延遲探索或執行對使用者體驗至關重要的指令碼。

版面配置所需的第三方程式庫應盡早載入以轉譯網頁。初始轉譯不需要的第三方應延後,以免封鎖主執行緒中的其他處理程序。Lighthouse 進行兩項稽核,用於標記會妨礙轉譯的指令碼或主執行緒封鎖的指令碼。

運用 Lighthouse 稽核,排除禁止轉譯資源並盡量減少第三方用量

請務必考量網頁上的資源載入順序,以免重要資源遭到延遲,而非關鍵資源也不會封鎖重要資源。

雖然有一些最佳做法可降低第三方的影響,但並非人人都可能知道如何為每位第三方導入這些規範。這種情形可能很複雜,原因如下:

  • 平均而言,網站會在行動裝置和電腦上使用21 至 23 個不同的第三方服務 (包括指令碼)。兩者的用法和建議可能不同。
  • 視使用特定架構或 UI 程式庫而定,實作許多第三方廠商可能會有所不同。
  • 較新的第三方程式庫經常推出。
  • 因為與同一第三方有關的業務需求有所改變,開發人員更難標準化使用資料的方式。

Aurora 著重於第三方指令碼

Aurora 與開放原始碼網路架構與工具合作,是提供強大預設值和認可的工具,可協助開發人員改善使用者體驗的各方面,例如效能、無障礙功能、安全性和行動裝置可用性。2021 年,我們著重於協助架構堆疊改善使用者體驗,以及改善網站體驗核心指標指標。

達成提高架構成效的目標最重要步驟之一,就是在 Next.js 中研究出理想的第三方指令碼載入序列。Next.js 等架構具有獨特優勢,可提供實用的預設值和功能,協助開發人員有效率地載入包含第三方的資源。我們研究了大量的 HTTP 封存和 Lighthouse 資料,找出哪些第三方禁止轉譯多數不同架構,

為解決主執行緒封鎖應用程式中第三方指令碼的問題,我們建構了指令碼元件。這個元件包含排序功能,可讓開發人員更妥善地控制第三方指令碼載入。

無需架構元件,即可序列第三方指令碼

如果想減少會阻斷轉譯的指令碼影響,可用的指南提供了下列有效載入及排序第三方指令碼的方法:

  1. asyncdefer 屬性與 <script> 標記搭配使用,指示瀏覽器在不封鎖文件剖析器的情況下載入非關鍵的第三方指令碼。初次載入網頁時不需要使用指令碼,或是初次與使用者互動的指令碼都可視為不重要。

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. 使用預先連線和 DNS 預先擷取功能,建立連線至必要來源的早期連線。這樣一來,重要指令碼就可以提早開始下載。

       <head>
           <link rel="preconnect" href="http://PreconnThis.com">
           <link rel="dns-prefetch" href="http://PrefetchThis.com">
       </head>
    
  3. 延遲載入第三方資源:在主要頁面內容載入完成,或使用者向下捲動到要納入網頁的部分時,這類資源會嵌入。

Next.js 指令碼元件

Next.js Script 元件會實作上述的序列指令碼方法,並提供開發人員定義載入策略的範本。指定合適的策略後,它會在不封鎖其他關鍵資源的情況下以最佳方式載入。

指令碼元件是以 HTML <script> 標記為基礎,並提供使用策略屬性為第三方指令碼設定載入優先順序的選項。

// Example for beforeInteractive:
<Script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" strategy="beforeInteractive" />

// Example for afterInteractive (default):
<Script src="https://example.com/samplescript.js" />

// Example for lazyonload:
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

策略屬性可包含三個值。

  1. beforeInteractive:這個選項可用於在網頁設為互動前執行的重要指令碼。Next.js 會確保這類指令碼被插入伺服器的初始 HTML 中,並在其他自我封裝的 JavaScript 之前執行。對於轉譯重要內容所需的同意聲明管理、機器人偵測指令碼或輔助程式庫,就很適合採用這項策略。

  2. afterInteractive:這是套用的預設策略,等同於使用延遲屬性載入指令碼。用於瀏覽器可在網頁互動後執行的指令碼 (例如分析指令碼)。Next.js 會將這些指令碼插入用戶端,且會在頁面加水後執行。因此,除非另有指定,否則使用指令碼元件定義的所有第三方指令碼都會由 Next.js 延遲,因此提供完善的預設值。

  3. lazyOnload:這個選項可用於在瀏覽器閒置時,延遲載入低優先順序的指令碼。網頁互動後 (例如即時通訊或社群媒體外掛程式),這類指令碼提供的功能不必在網頁互動後立即就能使用。

開發人員可以指定策略,告訴 Next.js 應用程式如何使用指令碼。如此一來,架構就能套用最佳化設定和最佳做法來載入指令碼,同時確保最佳載入順序。

使用 Script 元件時,開發人員可以將第三方指令碼置入應用程式任意位置,以延遲載入第三方,並在文件層級處理重要指令碼。這表示指令碼元件可使用指令碼與元件並置。飲水後,視使用的策略而定,系統會將指令碼插入最初呈現的文件標題或內文底部。

評估成效

我們運用 Next.js 商務應用程式入門網誌的範本,製作出兩個試用版應用程式,協助評估加入第三方指令碼的成效。最初用於 Google 代碼管理工具和社群媒體嵌入的第三方功能,會先直接加入這些應用程式網頁中,再透過指令碼元件加入。然後,比較這些網頁在 WebPageTest 上的成效。

Next.js 商務應用程式中的第三方指令碼

下方示範將第三方指令碼新增至商務應用程式範本中。

使用前 使用後
Google 代碼管理工具 (具備非同步功能) 兩個指令碼都有策略 = afterInteractive 的指令碼元件
沒有非同步或延遲的 Twitter 追蹤按鈕
示範 1 的指令碼和指令碼元件設定 (使用 2 個指令碼)。

以下比較顯示兩個版本的 Next.js commerce Starter-kit 視覺進度。如上所述,LCP 幾乎在前 1 秒發生,且已啟用正確載入策略的 Script 元件。

顯示 LCP 即興表演的幻燈片比較

Next.js 網誌中的第三方指令碼

第三方指令碼已新增至示範網誌應用程式,如下所示。

使用前 使用後
Google 代碼管理工具 (具備非同步功能) 使用策略 = 延遲載入,分別設定四個指令碼的指令碼元件
含有非同步動作的 Twitter 追蹤按鈕
YouTube「訂閱」按鈕,沒有非同步或延後通知
LinkedIn 追蹤按鈕沒有非同步或延遲
示範 2 的指令碼和指令碼元件設定 (包含 4 個指令碼)。
影片:顯示包含及沒有指令碼元件的索引頁面載入進度。使用指令碼元件時,FCP 縮短 0.5 秒。

如影片所示,首次顯示內容所需時間 (FCP) 是在不含指令碼元件的網頁上播放 0.9 秒,使用指令碼元件時則為 0.4 秒。

指令碼元件的後續規畫

雖然 afterInteractivelazyOnload 的策略選項可讓您大幅控管會封鎖的指令碼,但我們也在研究其他方法,希望可以增加 Script 元件的實用性。

使用網路工作站

網路工作處理程序可在背景執行緒上執行獨立指令碼,讓主執行緒有更多時間處理處理使用者介面工作及提升效能。Web Worker 最適合從主執行緒卸載 JavaScript 處理作業,而非 UI 工作。用於客戶服務或行銷的指令碼 (通常不會與 UI 互動) 或許適合在背景執行緒中執行。輕量的第三方程式庫 (PartyTown) 可用於將這類指令碼隔離到網路工作站。

根據目前實作的 Next.js 指令碼元件,建議您將策略設為 afterInteractivelazyOnload,在主執行緒上延遲這些指令碼。我們日後建議您推出全新策略選項 'worker',方便 Next.js 使用 PartyTown 或自訂解決方案在網路工作站中執行指令碼。我們歡迎開發人員對此 RFC 提供意見。

盡量減少 CLS

第三方嵌入內容 (例如廣告、影片或社群媒體動態消息嵌入) 可能會在延遲載入時造成版面配置位移。這會影響使用者體驗和網頁的累計版面配置位移 (CLS) 指標。您可以指定用於載入嵌入的容器大小,藉此將 CLS 降到最低。

指令碼元件可用於載入可能導致版面配置位移的嵌入項目。我們正在考慮擴充這項功能,以提供有助於減少 CLS 的設定選項。您可以在指令碼元件本身或隨附元件中提供此內容。

包裝函式元件

加入熱門第三方指令碼 (例如 Google Analytics (分析) 或 Google 代碼管理工具 (GTM)) 的語法和載入策略通常已修正。針對每個類型的指令碼,您可以進一步封裝在個別包裝函式元件中。開發人員只能使用極少量的應用程式專屬屬性 (例如追蹤 ID)。包裝函式元件可為開發人員帶來以下優勢:

  1. 輕鬆加入熱門的指令碼標記。
  2. 確保架構整體採用最適當的策略。

結論

第三方指令碼通常是為了在使用者瀏覽的網站上提供特定功能。為了減少非重要指令碼的影響,建議您延後這些指令碼 (根據預設,Next.js 指令碼元件會執行此作業)。開發人員可以保證,除非明確採用 beforeInteractive 策略,否則內含的指令碼不會延遲重要功能。如同 Next.js 指令碼元件,架構開發人員也可以考慮在其他架構中建構這些功能。我們正與 Nuxt.js 團隊一起積極探索類似的到達網頁。我們也希望根據意見回饋,進一步強化指令碼元件,進而涵蓋更多用途。

特別銘謝

感謝 Kara EricksonJanicklas RalphKatie HempeniusPhilip WaltonJeremy WagnerAddy Osmani 對這篇貼文提供意見。