Next.js でのサードパーティ スクリプトの読み込みを最適化する

サードパーティ スクリプトの読み込みを最適化する組み込みソリューションを提供する Next.js の Script コンポーネントの背景について説明します。

モバイルとデスクトップで配信されるウェブサイトからのリクエストの約 45% はサードパーティのリクエストであり、そのうち 33% はスクリプトです。サードパーティ スクリプトのサイズ、レイテンシ、読み込みは、サイトのパフォーマンスに重大な影響を与える可能性があります。Next.js Script コンポーネントには、ベスト プラクティスとデフォルトが組み込まれているため、デベロッパーはアプリケーションにサードパーティ スクリプトを導入しながら、潜在的なパフォーマンスの問題にすぐに対処できます。

サードパーティのスクリプトとそれによるパフォーマンスへの影響

サードパーティ スクリプトを使用すると、ウェブ デベロッパーは既存のソリューションを活用して一般的な機能を実装し、開発時間を短縮できます。しかし、これらのスクリプトの作成者は、通常、利用するウェブサイトのパフォーマンスへの影響を考慮する動機を持ちません。これらのスクリプトは、使用するデベロッパーにとってもブラックボックスです。

スクリプトは、さまざまなカテゴリのサードパーティ リクエストのウェブサイトからダウンロードされるサードパーティ バイト数を多く占めています。デフォルトでは、ブラウザはドキュメント内の場所に基づいてスクリプトに優先順位を付けるため、ユーザー エクスペリエンスにとって重要なスクリプトの検出や実行が遅れる可能性があります。

レイアウトに必要なサードパーティ ライブラリを、ページをレンダリングするために早めに読み込む必要があります。最初のレンダリングに不要なサードパーティは、メインスレッドの他の処理をブロックしないように延期する必要があります。Lighthouse では、2 つの監査を実施して、レンダリング ブロック スクリプトまたはメインスレッド ブロック スクリプトを報告します。

Lighthouse 監査により、レンダリングをブロックするリソースを排除し、サードパーティの使用を最小限に抑える

ページのリソースの読み込み順序に注意を払い、重要なリソースが遅延しないようにし、重要でないリソースが重要なリソースをブロックしないようにすることが重要です。

サードパーティによる影響を軽減するためのベスト プラクティスはありますが、使用するサードパーティすべてに実装する方法を誰もが理解しているとは限りません。これは、次のような理由で複雑になることがあります。

  • ウェブサイトでは、モバイルとパソコンで平均して 21 ~ 23 種類のサードパーティ(スクリプトを含む)を利用しています。使用方法や推奨事項は、デバイスによって異なる場合があります。
  • 多くのサードパーティの実装は、特定のフレームワークまたは UI ライブラリを使用するかどうかによって異なります。
  • 新しいサードパーティ ライブラリが頻繁に導入されています。
  • 同じサードパーティに関連するさまざまなビジネス要件により、デベロッパーがその使用を標準化することが難しくなっています。

サードパーティ スクリプトに対する Aurora の注目

Aurora はオープンソースのウェブ フレームワークやツールとのコラボレーションの一環として、デベロッパーがパフォーマンス、ユーザー補助、セキュリティ、モバイル対応など、ユーザー エクスペリエンスの側面を改善できるように、強力なデフォルトと独自のツールを提供しています。2021 年、Google はフレームワーク スタックによるユーザー エクスペリエンスと Core Web Vitals の指標の改善に注力してきました。

フレームワークのパフォーマンス改善という目標を達成するための最も重要なステップの一つは、Next.js でサードパーティ スクリプトの理想的な読み込み順序を調査することでした。Next.js などのフレームワークは独自の立場にあり、デベロッパーがサードパーティを含むリソースを効率的に読み込むのに役立つ便利なデフォルト値と機能を提供しています。Google は、HTTP Archive と Lighthouse のデータを幅広く調査し、さまざまなフレームワークで最も多くレンダリングをブロックしているサードパーティを特定しました。

アプリケーションで使用されるサードパーティ スクリプトがメインスレッドをブロックするという問題に対処するために、スクリプト コンポーネントを作成しました。このコンポーネントはシーケンス機能をカプセル化して、デベロッパーがサードパーティ スクリプトの読み込みをより適切に制御できるようにしています。

フレームワーク コンポーネントを使用しないサードパーティ スクリプトの順序付け

レンダリングをブロックするスクリプトの影響を軽減するためのガイダンスでは、サードパーティ スクリプトを効率的に読み込み、順序付けするための次の方法を提供しています。

  1. async 属性または defer 属性を <script> タグとともに使用すると、ドキュメント パーサーをブロックせずに重要でない第三者スクリプトを読み込むようブラウザに指示できます。最初のページ読み込みや最初のユーザー操作に不要なスクリプトは、重要でないと見なされる場合があります。

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. preconnect と dns-prefetch を使用して、必要な送信元への早期接続を確立します。これにより、重要なスクリプトのダウンロードを早期に開始できます。

       <head>
           <link rel="preconnect" href="http://PreconnThis.com">
           <link rel="dns-prefetch" href="http://PrefetchThis.com">
       </head>
    
  3. サードパーティのリソースや埋め込みは、メインページのコンテンツの読み込みが終了した後、またはページ内でそれらが含まれる部分まで下にスクロールしたときに遅延読み込みを行います。

Next.js Script コンポーネント

Next.js スクリプト コンポーネントは、スクリプトの順序付けのために上記のメソッドを実装し、デベロッパーが読み込み方法を定義するためのテンプレートを提供します。適切な戦略を指定すると、他の重要なリソースをブロックせずに、最適な方法で読み込まれます。

Script コンポーネントは HTML <script> タグ上に構築されており、strategy 属性を使用してサードパーティ スクリプトの読み込み優先度を設定できるオプションが用意されています。

// 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" />

戦略属性には 3 つの値を指定できます。

  1. beforeInteractive: このオプションは、ページがインタラクティブになる前に実行する必要がある重要なスクリプトに使用できます。Next.js では、このようなスクリプトがサーバー上の最初の HTML に挿入され、他の自己バンドル型 JavaScript の前に実行されることが保証されます。重要なコンテンツのレンダリングに必要な同意管理、bot 検出スクリプト、ヘルパー ライブラリは、この戦略に適した候補です。

  2. afterInteractive: これは適用されるデフォルトの戦略であり、defer 属性を持つスクリプトを読み込むのと同じです。分析スクリプトなど、ページがインタラクティブになった後にブラウザが実行できるスクリプトに使用します。Next.js はこれらのスクリプトをクライアントサイドに挿入し、ページがハイドレートされた後に実行されます。特に指定しない限り、Script コンポーネントを使用して定義されたすべてのサードパーティ スクリプトは、Next.js によって遅延されるため、強力なデフォルトが提供されます。

  3. lazyOnload: このオプションを使用すると、ブラウザがアイドル状態のときに優先度の低いスクリプトを遅延読み込みできます。このようなスクリプトが提供する機能(チャット プラグインやソーシャル メディア プラグインなど)は、ページがインタラクティブになった直後に必要ありません。

デベロッパーは、戦略を指定することで、アプリケーションでどのようにスクリプトを使用するかを Next.js に指示できます。これにより、フレームワークは最適な読み込みシーケンスを確保しながら、最適化とベスト プラクティスを適用してスクリプトを読み込むことができます。

Script コンポーネントを使用すると、デベロッパーはサードパーティを遅延読み込みするためにアプリケーションの任意の場所にサードパーティ スクリプトを配置でき、重要なスクリプトについてはドキュメント レベルでスクリプトを配置できます。これは、スクリプトを使用するコンポーネントとスクリプト コンポーネントが同じ場所に配置される可能性があることを意味します。ハイドレーション後、スクリプトは、使用する戦略に応じて、最初にレンダリングされたドキュメントのヘッドまたは本文の下部に挿入されます。

効果の測定

Next.js のコマースアプリスターター ブログのテンプレートを使用して、サードパーティ スクリプトを組み込んだ場合の効果を測定するデモアプリを 2 つ作成しました。Google タグ マネージャーとソーシャル メディアの埋め込みによく使用されるサードパーティが、最初はこれらのアプリのページに直接組み込まれ、その後スクリプト コンポーネントを介して含まれていました。次に、WebPageTest でこれらのページのパフォーマンスを比較しました。

Next.js コマースアプリのサードパーティ スクリプト

以下のとおり、このデモ用のコマース アプリ テンプレートにサードパーティのスクリプトを追加しました。

適用前 適用後
Google タグ マネージャー(非同期) 両方のスクリプトで strategy = afterInteractive を設定したスクリプト コンポーネント
Twitter のフォローボタン(非同期または遅延なし)
デモ 1 のスクリプトとスクリプト コンポーネントの構成(2 つのスクリプトを使用)。

次の比較では、Next.js commerce starter-kit の両方のバージョンを比較し、進行状況を示します。ご覧のように、適切な読み込み戦略でスクリプト コンポーネントを有効にすると、LCP は 1 秒近く早くなります。

LCP の進歩を示すフィルム ストリップの比較

Next.js ブログのサードパーティ スクリプト

デモブログアプリにサードパーティのスクリプトを以下のように追加しました。

適用前 適用後
Google タグ マネージャー(非同期) 4 つのスクリプトそれぞれに対してストラテジー = 遅延読み込みが設定されたスクリプト コンポーネント
Twitter のフォローボタン(非同期あり)
YouTube のチャンネル登録ボタンに async または defer がない
LinkedIn のフォローボタン(非同期または遅延なし)
デモ 2 のスクリプトとスクリプト コンポーネントの構成(4 つのスクリプト)。
Script コンポーネントがある場合とない場合のインデックス ページの読み込み状況を示す動画。Script コンポーネントを使用すると、FCP が 0.5 秒改善されます。

動画からわかるように、First Contentful Paint(FCP)は、Script コンポーネントを使用しない場合の 0.9 秒、Script コンポーネントを使用した場合の 0.4 秒で発生します。

スクリプト コンポーネントの次のステップ

afterInteractivelazyOnload の戦略オプションではレンダリングをブロックするスクリプトを詳細に制御できますが、スクリプト コンポーネントの有用性を高める他のオプションも検討しています。

ウェブワーカーの使用

ウェブ ワーカーを使用すると、バックグラウンド スレッドで独立したスクリプトを実行できるため、メインスレッドを解放してユーザー インターフェース タスクの処理を処理できるようになり、パフォーマンスが向上します。ウェブ ワーカーは、UI 処理ではなく JavaScript 処理をメインスレッドからオフロードする場合に最適です。カスタマー サポートやマーケティングに使用するスクリプトは、通常は UI とやり取りしないため、バックグラウンド スレッドで実行するのがよいでしょう。軽量のサードパーティ ライブラリ(PartyTown)を使用すると、このようなスクリプトをウェブワーカーに分離できます。

Next.js スクリプト コンポーネントの現在の実装では、戦略を afterInteractive または lazyOnload に設定して、これらのスクリプトをメインスレッドで延期することをおすすめします。将来的には、新しい戦略オプション 'worker' を導入する予定です。これにより、Next.js は PartyTown またはカスタム ソリューションを使用してウェブワーカーでスクリプトを実行できるようになります。この RFC に関するデベロッパーからのコメントを歓迎します。

CLS の最小化

広告、動画、ソーシャル メディア フィードの埋め込みなど、サードパーティの埋め込みは遅延読み込み時にレイアウト シフトを引き起こすことがあります。これは、ページのユーザー エクスペリエンスと Cumulative Layout Shift(CLS)指標に影響します。CLS を最小限に抑えるには、埋め込みを読み込むコンテナのサイズを指定します。

スクリプト コンポーネントは、レイアウト シフトを引き起こす可能性がある埋め込みを読み込むために使用されることがあります。CLS の削減に役立つ構成オプションを提供できるよう、この機能を拡張することを検討しています。これは、Script コンポーネント自体の内部で、またはコンパニオン コンポーネントとして使用できます。

ラッパー コンポーネント

Google アナリティクスや Google タグ マネージャー(GTM)などの一般的なサードパーティ スクリプトを組み込む場合、構文と読み込み方法は通常固定されています。これらは、スクリプトの種類ごとに個別のラッパー コンポーネントにさらにカプセル化できます。デベロッパーは、アプリ固有の属性(トラッキング ID など)の最小限のセットのみを使用できます。ラッパー コンポーネントには次のようなメリットがあります。

  1. 一般的なスクリプトタグを簡単に追加できるようにしました。
  2. フレームワークが内部的に最適な戦略を使用するようにする。

おわりに

サードパーティのスクリプトは通常、ユーザーのウェブサイトに特定の機能を含めるために作成されます。重要性の低いスクリプトの影響を軽減するため、それらのスクリプトは延期することをおすすめします。Next.js Script コンポーネントはデフォルトで行います。デベロッパーは、明示的に beforeInteractive 戦略を適用しない限り、スクリプトを組み込んでも重要な機能が遅延しないことを保証できます。Next.js Script コンポーネントと同様に、フレームワーク デベロッパーは、これらの機能を他のフレームワークで構築することも検討できます。Google は、Nuxt.js チームとともに同様のコンポーネントの実装を積極的に検討しています。また、フィードバックに基づいて、スクリプト コンポーネントをさらに強化し、他のユースケースに対応できるようにしたいと考えています。

謝辞

この投稿に関するフィードバックを提供してくれた Kara EricksonJanicklas RalphKatie HempeniusPhilip WaltonJeremy WagnerAddy Osmani に感謝します。