瞭解 Next.js 指令碼元件背後的願景,這個內建解決方案可以最佳化第三方指令碼的載入作業。
在行動裝置和電腦上放送的網站所送出的請求中,約有 45% 是第三方要求,其中 33% 是指令碼。第三方指令碼的大小、延遲及載入作業可能會大幅影響網站效能。Next.js 指令碼元件提供內建的最佳做法和預設值,可協助開發人員在應用程式中導入第三方指令碼,同時可立即解決潛在的效能問題。
第三方指令碼及其對成效的影響
第三方指令碼可讓網頁開發人員利用現有解決方案導入常用功能,並縮短開發時間。不過,這些指令碼的製作者通常沒有誘因,無法判斷使用者在網站上的成效影響。這些指令碼也是黑箱作業,可供開發人員使用。
指令碼佔了網站以各種第三方要求類別下載的大量第三方位元組。根據預設,瀏覽器會根據文件內容決定指令碼的優先順序,因此可能會延遲發現或執行重要的指令碼,進而影響使用者體驗。
版面配置需要的第三方程式庫必須提早載入,才能顯示網頁。對初始轉譯不需要的第三方應延後處理,以免在主執行緒上阻擋其他處理程序。Lighthouse 進行兩項稽核作業,用來標記會阻擋轉譯或主執行緒封鎖的指令碼。
請務必考量網頁的資源載入順序,避免重要資源延遲,非關鍵資源也不會封鎖重要資源。
雖然我們已經採取最佳做法來減少第三方造成的影響,但也不清楚如何為每個第三方採用這些最佳做法。可能的原因如下:
- 平均而言,電腦版和電腦版網站會使用 21 至 23 個不同的第三方 (包括指令碼)。每項工具的用法和建議可能不盡相同。
- 視使用特定架構或 UI 程式庫而定,許多第三方的實作方式可能有所不同。
- 我們經常推出較新的第三方程式庫。
- 同一第三方必須因應不同的業務需求,開發人員很難將相關用途標準化。
Aurora 聚焦於第三方指令碼
Aurora 與開放原始碼網路架構和工具進行合作的其中一環,是提供強大的預設和質疑工具,協助開發人員改善使用者體驗的各個層面,例如效能、無障礙功能、安全性和行動裝置完備性。2021 年,我們致力協助架構堆疊來改善使用者體驗和網站體驗核心指標指標。
要達成目標,改善架構成效的最重要步驟之一,就是在 Next.js 中研究第三方指令碼的理想載入順序。Next.js 等架構發揮獨特優勢,能夠提供實用的預設值和功能,協助開發人員有效率地載入資源,包括第三方。我們進行了廣泛的 HTTP 封存和 Lighthouse 資料,找出哪些第三方在不同架構中無法轉譯最多。
為解決因主執行緒封鎖應用程式中所用第三方指令碼的問題,我們打造了指令碼元件。這個元件會封裝序列功能,為開發人員提供更完善的第三方指令碼載入控制項。
在沒有架構元件的情況下將第三方指令碼序列
您可以參考可用指南,減少轉譯封鎖指令碼造成的影響,提供以下方法有效率地載入第三方指令碼及排定排序:
搭配
<script>
標記使用async
或defer
屬性,告知瀏覽器在不封鎖文件剖析器的情況下,載入非關鍵的第三方指令碼。初次載入網頁時不需要的指令碼,或最初使用者互動時不需要的指令碼,系統可能會將其視為不重要。<script src="https://example.com/script1.js" defer></script> <script src="https://example.com/script2.js" async></script>
使用預先連線和 dns-prefetch 的預先連線至必要來源的早期連線。如此一來,重要指令碼就能更早開始下載。
<head> <link rel="preconnect" href="http://PreconnThis.com"> <link rel="dns-prefetch" href="http://PrefetchThis.com"> </head>
在主要網頁內容載入完成,或使用者向下捲動至要納入的網頁部分時,延遲載入第三方資源和嵌入項目。
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" />
策略屬性可接受三個值。
beforeInteractive
:這個選項可用於在網頁變成互動之前執行的重要指令碼。Next.js 可確保這類指令碼會插入伺服器的初始 HTML,並在其他自行組合的 JavaScript 之前執行。採用此策略時,應使用同意聲明管理、機器人偵測指令碼或輔助程式庫。afterInteractive
:此為預設策略,等同於載入含有延遲屬性的指令碼。應用於瀏覽器可在網頁互動後執行的指令碼,例如分析指令碼。Next.js 會在用戶端插入這些指令碼,然後在網頁解除水分後執行。因此,除非另有指定,否則所有使用指令碼元件定義的第三方指令碼都將由 Next.js 延遲,從而提供強大的預設值。lazyOnload
:這個選項可用於在瀏覽器閒置時延遲載入低優先順序的指令碼。網頁啟用互動功能 (例如即時通訊或社群媒體外掛程式) 後,不一定要立即使用這類指令碼提供的功能。
開發人員可以透過指定策略,告知 Next.js 應用程式如何使用指令碼。這樣一來,架構就能套用最佳化措施和最佳做法來載入指令碼,同時確保載入序列最好。
使用指令碼元件時,開發人員可將第三方指令碼置入應用程式的任何位置,以便延遲第三方載入第三方,以及針對重要指令碼在文件層級執行這項作業。這表示指令碼元件可透過指令碼與元件一起放置。喝水後,視所用策略而定,指令碼會插入至初始轉譯文件的標題或內文底部。
評估成效
我們使用 Next.js 商務應用程式和入門網誌的範本來製作兩個試用版應用程式,藉此評估加入第三方指令碼的影響。常見的第三方代碼和社群媒體嵌入項目已直接納入應用程式的頁面,再透過指令碼元件進行導入。接著,我們會比較這些網頁在 WebPageTest 上的效能。
Next.js 商務應用程式中的第三方指令碼
按照下方說明,在試用版商務應用程式範本中新增第三方指令碼。
以下比較表顯示這兩種版本的 Next.js commerce Starter-kit 視覺進度。如您所見,在啟用指令碼元件並採用正確的載入策略的情況下,LCP 觸發時間將近 1 秒。
Next.js 網誌中的第三方指令碼
已按照下方說明將第三方指令碼新增至示範網誌應用程式。
完成前 | 完成後 |
---|---|
Google 代碼管理工具和非同步 | 指令碼元件,其策略為四個指令碼分別有延遲載入 |
Twitter 追蹤按鈕 (非同步) | |
YouTube 訂閱按鈕,沒有非同步或延遲顯示 | |
LinkedIn 追蹤按鈕,沒有非同步或延遲 |
如影片所示,首次顯示內容所需時間 (FCP) 是發生在沒有指令碼元件的網頁 0.9 秒,以及指令碼元件的 0.4 秒。
指令碼元件的後續步驟
雖然 afterInteractive
和 lazyOnload
的策略選項可讓您大幅控制轉譯封鎖指令碼,但我們也會探索其他可以提升指令碼元件實用性的選項。
使用 Web Worker
Web Worker 可用於在背景執行緒上執行獨立的指令碼,進而釋出主執行緒來處理使用者介面工作,並提升效能。Web Worker 最適合在主執行緒外卸載 JavaScript 處理作業,而非 UI 工作。用於客戶服務或行銷的指令碼 (通常不會與使用者介面互動) 可能適合在背景執行緒中執行。輕量的第三方程式庫 PartyTown 可用來將這類指令碼隔離至網路工作站。
根據目前實作的 Next.js 指令碼元件,建議您將策略設為 afterInteractive
或 lazyOnload
,以便在主執行緒中延遲這些指令碼。我們日後提議推出新的策略選項 'worker'
,可讓 Next.js 使用 PartyTown 或自訂解決方案在網路工作站上執行指令碼。我們歡迎開發人員針對這個 RFC 提供意見。
最小化 CLS
嵌入於第三方嵌入內容 (例如廣告、影片或社群媒體動態消息) 可能會導致延遲載入時版面配置位移。這會影響使用者體驗和網頁的「累計版面配置位移」(CLS) 指標。您可以指定嵌入項目要載入的容器大小,藉此將 CLS 最小化。
指令碼元件可用於載入可能導致版面配置位移的嵌入內容。我們打算進一步擴充這項功能,以便提供有助於減少 CLS 的設定選項。您可以在指令碼元件內提供這項程式碼,或是以隨附元件的形式提供。
包裝函式元件
加入 Google Analytics (分析) 或 Google 代碼管理工具 (GTM) 等熱門第三方指令碼的語法和載入策略,通常都是固定的。您可以針對每種指令碼類型,在個別包裝函式元件中進一步封裝。開發人員只能取得一小部分的應用程式專屬屬性 (例如追蹤 ID)。包裝函式元件可為開發人員提供下列協助:
- 方便他們加入熱門的指令碼標記。
- 確保架構在運作時採用最佳策略。
結語
建立第三方指令碼的目的,通常是為了納入網站特定功能中的特定功能。如要降低非重要指令碼的影響,建議您延遲這些指令碼 (Next.js 指令碼元件預設為執行)。開發人員可以保證,加入指令碼不會延遲重要功能,除非明確套用 beforeInteractive
策略。架構開發人員也可以考慮透過其他架構建構這些功能,就像 Next.js 指令碼元件一樣。我們目前正與 Nuxt.js 團隊共同探索類似的元件。我們也希望根據使用者的意見回饋,進一步改善 指令碼元件,以涵蓋其他用途。
認證
感謝 Kara Erickson、Janicklas Ralph、Katie Hempenius、Philip Walton、Jeremy Wagner 及 Addy Osmani 對這篇貼文的意見。