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

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

Leena Sohoni
Leena Sohoni

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

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

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

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

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

Lighthouse 稽核:排除會妨礙顯示的資源和盡量減少第三方程式碼的使用量

請務必考量網頁的資源載入順序,以免關鍵資源延遲,且非關鍵資源不會阻斷關鍵資源。

雖然有最佳做法可用來降低第三方程式碼的影響,但並非所有人都知道如何針對所使用的每個第三方程式碼實作這些做法。這可能會變得複雜,原因如下:

  • 平均來說,網站會在行動版和電腦版上使用 21 到 23 個不同的第三方服務 (包括指令碼)。兩者的用法和建議可能不同。
  • 實作許多第三方服務時,會因是否使用特定架構或 UI 程式庫而有所不同。
  • 我們經常推出新的第三方程式庫。
  • 由於與同一第三方相關的業務需求各不相同,開發人員難以將其使用方式標準化。

Aurora 著重於第三方指令碼

Aurora 與開源網頁架構和工具的合作,其中一部分是提供強大的預設值和自訂工具,協助開發人員改善使用者體驗的各個層面,例如效能、無障礙設計、安全性和行動裝置就緒性。2021 年,我們專注於協助架構堆疊改善使用者體驗和網站使用體驗核心指標

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

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

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

可用的指南可減少轉譯阻斷指令碼的影響,提供以下方法,以便有效率地載入及排序第三方指令碼:

  1. 請在 <script> 標記中使用 asyncdefer 屬性,告知瀏覽器載入非關鍵的第三方指令碼,且不阻擋文件剖析器。初始網頁載入或首次使用者互動時,系統可能會將不必要的指令碼視為非必要指令碼。

       <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://cdnjs.cloudflare.com/polyfill/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" />

strategy 屬性可接受三個值。

  1. beforeInteractive:這個選項可用於必須在網頁變成互動式前執行的重要指令碼。Next.js 會確保這類指令碼會注入伺服器上的初始 HTML,並在其他自行內含的 JavaScript 之前執行。如要顯示重要內容,同意聲明管理、機器人偵測指令碼或輔助程式庫,都是採用這項策略的理想選擇。

  2. afterInteractive:這是套用的預設策略,等同於使用延遲屬性載入指令碼。應將其用於瀏覽器可在網頁可供互動後執行的腳本,例如數據分析腳本。Next.js 會在用戶端上插入這些指令碼,並在網頁完成復原後執行。因此,除非另有指定,否則所有使用 Script 元件定義的第三方指令碼都會由 Next.js 延後執行,進而提供強大的預設值。

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

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

開發人員可以使用 Script 元件,在應用程式中任何位置放置第三方指令碼,以便延後載入第三方,並在文件層級放置關鍵指令碼。這表示指令碼元件可能與使用指令碼的元件一同放置。重新整理後,系統會根據所用策略,將指令碼插入最初轉譯文件的標頭或內文底部。

評估成效

我們使用 Next.js 商務應用程式入門網誌的範本製作兩個試用版應用程式,以評估加入第三方指令碼的成效影響。我們一開始直接在這些應用程式的網頁中加入 Google 代碼管理工具和社群媒體嵌入項目常用的第三方服務,之後再透過「程式碼」元件加入。然後,比較這些網頁在 WebPageTest 上的成效。

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

我們在示範用的商務應用程式範本中加入了第三方指令碼,如下所示。

之前 使用後
使用非同步的 Google 代碼管理工具 指令碼元件,兩個指令碼的策略皆為 afterInteractive
不使用非同步或延遲的 Twitter 追蹤按鈕
示範 1 的腳本和腳本元件設定 (含 2 個腳本)。

下列比較圖表顯示 Next.js 商務啟動包兩個版本的視覺進度。如圖所示,啟用指令碼元件並採用正確的載入策略後,LCP 發生時間提早了近 1 秒。

比較圖表顯示 LCP 改善情形

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

我們已將第三方指令碼加入示範網誌應用程式,如下所示。

之前 使用後
使用非同步的 Google 代碼管理工具 為四個指令碼設定 strategy = lazyonload 的指令碼元件
使用非同步的 Twitter 追蹤按鈕
YouTube「訂閱」按鈕,沒有非同步或延後通知
LinkedIn 追蹤按鈕 (不使用異步或延遲)
示範 2 的腳本和腳本元件設定,其中包含 4 個腳本。
這部影片展示了索引頁面載入進度,其中包含和不包含指令碼元件的情況。使用 Script 元件後,FCP 改善了 0.5 秒。

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

指令碼元件的後續規畫

雖然 afterInteractivelazyOnload 的策略選項可有效控管轉譯阻斷指令碼,但我們也正在研究其他可提高指令碼元件效用的選項。

使用 Web Workers

Web worker 可用於在背景執行緒上執行獨立的指令碼,藉此釋出主執行緒,以便處理使用者介面工作,並提升效能。Web Workers 最適合從主執行緒中卸載 JavaScript 處理作業,而非 UI 工作。用於客戶服務或行銷的指令碼 (通常不會與 UI 互動) 或許適合在背景執行緒中執行。您可以使用輕量版第三方程式庫 PartyTown,將這類指令碼隔離到網路工作站。

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

盡量降低 CLS

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

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

包裝函式元件

加入 Google Analytics 或 Google 代碼管理工具 (GTM) 等熱門第三方指令碼的語法和載入策略通常是固定的。這些元素可進一步封裝在各個腳本類型的個別包裝函式元件中。開發人員只能使用一組最少量的應用程式專屬屬性 (例如追蹤 ID)。包裝函式元件可協助開發人員:

  1. 讓他們更輕鬆地加入熱門指令碼標記。
  2. 確保架構在幕後使用最理想的策略。

結論

第三方指令碼通常是為了在使用網站中加入特定功能而建立。為降低非必要指令碼的影響,建議您延後執行這些指令碼,Next.js 指令碼元件預設會執行這項操作。開發人員可以確保,除非明確套用 beforeInteractive 策略,否則所附加的指令碼不會延遲重要功能。如同 Next.js 指令碼元件,架構開發人員也可以考慮在其他架構中建構這些功能。我們正與 Nuxt.js 團隊一起積極探索類似的到達網頁。根據意見回饋,我們也希望進一步強化程式碼元件,以涵蓋更多使用情境。

特別銘謝

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