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

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

李娜索尼
Lena Sohoni
豪西恩吉德
Houssein Djirdeh

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

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

第三方指令碼可讓網頁開發人員利用現有解決方案導入常用功能,並縮短開發時間。不過,這些指令碼的製作者通常沒有誘因,無法判斷使用者在網站上的成效影響。這些指令碼也是黑箱作業,可供開發人員使用。

指令碼佔了網站以各種第三方要求類別下載的大量第三方位元組。根據預設,瀏覽器會根據文件內容決定指令碼的優先順序,因此可能會延遲發現或執行重要的指令碼,進而影響使用者體驗。

版面配置需要的第三方程式庫必須提早載入,才能顯示網頁。對初始轉譯不需要的第三方應延後處理,以免在主執行緒上阻擋其他處理程序。Lighthouse 進行兩項稽核作業,用來標記會阻擋轉譯或主執行緒封鎖的指令碼。

進行 Lighthouse 稽核,藉此排除會妨礙顯示的資源,並盡量減少第三方用量

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

雖然我們已經採取最佳做法來減少第三方造成的影響,但也不清楚如何為每個第三方採用這些最佳做法。可能的原因如下:

  • 平均而言,電腦版和電腦版網站會使用 21 至 23 個不同的第三方 (包括指令碼)。每項工具的用法和建議可能不盡相同。
  • 視使用特定架構或 UI 程式庫而定,許多第三方的實作方式可能有所不同。
  • 我們經常推出較新的第三方程式庫。
  • 同一第三方必須因應不同的業務需求,開發人員很難將相關用途標準化。

Aurora 聚焦於第三方指令碼

Aurora 與開放原始碼網路架構和工具進行合作的其中一環,是提供強大的預設和質疑工具,協助開發人員改善使用者體驗的各個層面,例如效能、無障礙功能、安全性和行動裝置完備性。2021 年,我們致力協助架構堆疊來改善使用者體驗和網站體驗核心指標指標。

要達成目標,改善架構成效的最重要步驟之一,就是在 Next.js 中研究第三方指令碼的理想載入順序。Next.js 等架構發揮獨特優勢,能夠提供實用的預設值和功能,協助開發人員有效率地載入資源,包括第三方。我們進行了廣泛的 HTTP 封存和 Lighthouse 資料,找出哪些第三方在不同架構中無法轉譯最多。

為解決因主執行緒封鎖應用程式中所用第三方指令碼的問題,我們打造了指令碼元件。這個元件會封裝序列功能,為開發人員提供更完善的第三方指令碼載入控制項。

在沒有架構元件的情況下將第三方指令碼序列

您可以參考可用指南,減少轉譯封鎖指令碼造成的影響,提供以下方法有效率地載入第三方指令碼及排定排序:

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

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. 使用預先連線和 dns-prefetch 的預先連線至必要來源的早期連線。如此一來,重要指令碼就能更早開始下載。

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

Next.js 指令碼元件

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

指令碼元件是以 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 應用程式如何使用指令碼。這樣一來,架構就能套用最佳化措施和最佳做法來載入指令碼,同時確保載入序列最好。

使用指令碼元件時,開發人員可將第三方指令碼置入應用程式的任何位置,以便延遲第三方載入第三方,以及針對重要指令碼在文件層級執行這項作業。這表示指令碼元件可透過指令碼與元件一起放置。喝水後,視所用策略而定,指令碼會插入至初始轉譯文件的標題或內文底部。

評估成效

我們使用 Next.js 商務應用程式入門網誌的範本來製作兩個試用版應用程式,藉此評估加入第三方指令碼的影響。常見的第三方代碼和社群媒體嵌入項目已直接納入應用程式的頁面,再透過指令碼元件進行導入。接著,我們會比較這些網頁在 WebPageTest 上的效能。

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

按照下方說明,在試用版商務應用程式範本中新增第三方指令碼。

完成前 完成後
Google 代碼管理工具和非同步 兩個指令碼的指令碼元件的策略為 afterInteractive
Twitter 追蹤按鈕,沒有非同步或延遲
示範 1 的指令碼和指令碼元件設定 (含有 2 個指令碼)。

以下比較表顯示這兩種版本的 Next.js commerce Starter-kit 視覺進度。如您所見,在啟用指令碼元件並採用正確的載入策略的情況下,LCP 觸發時間將近 1 秒。

顯示 LCP 即興表演的影片片段比較

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

已按照下方說明將第三方指令碼新增至示範網誌應用程式。

完成前 完成後
Google 代碼管理工具和非同步 指令碼元件,其策略為四個指令碼分別有延遲載入
Twitter 追蹤按鈕 (非同步)
YouTube 訂閱按鈕,沒有非同步或延遲顯示
LinkedIn 追蹤按鈕,沒有非同步或延遲
含有 4 個指令碼的示範 2 指令碼和指令碼元件設定。
影片:顯示是否包含指令碼元件的索引頁面載入進度。使用指令碼元件時,FCP 會增加 0.5 秒。

如影片所示,首次顯示內容所需時間 (FCP) 是發生在沒有指令碼元件的網頁 0.9 秒,以及指令碼元件的 0.4 秒。

指令碼元件的後續步驟

雖然 afterInteractivelazyOnload 的策略選項可讓您大幅控制轉譯封鎖指令碼,但我們也會探索其他可以提升指令碼元件實用性的選項。

使用 Web Worker

Web Worker 可用於在背景執行緒上執行獨立的指令碼,進而釋出主執行緒來處理使用者介面工作,並提升效能。Web Worker 最適合在主執行緒外卸載 JavaScript 處理作業,而非 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 對這篇貼文的意見。