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

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

Leena Sohoni 氏
Leena Sohoni

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

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

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

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

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

レンダリング ブロック リソースの排除とサードパーティの使用の最小化のための Lighthouse の監査

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

サードパーティによる影響を軽減するためのベスト プラクティスはありますが、利用するサードパーティごとにそれぞれ実践方法を知っているわけではありません。これは、次のような理由で複雑になることがあります。

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

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

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

フレームワークのパフォーマンス向上という目標を達成するための最も重要なステップの 1 つが、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 コンポーネントは、スクリプトをシーケンシングするための上記のメソッドを実装し、デベロッパーが読み込み戦略を定義するテンプレートを提供します。適切な戦略を指定すれば、他の重要なリソースをブロックすることなく、最適な方法で読み込まれます。

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 タグ マネージャーやソーシャル メディア埋め込みによく使用されるサードパーティは、最初はこれらのアプリのページに直接組み込まれ、次に Script コンポーネントを通して含まれていました。次に、WebPageTest でこれらのページのパフォーマンスを比較しました。

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

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

変更前 変更後
Google タグ マネージャーと async どちらのスクリプトでも、戦略が afterInteractive のスクリプト コンポーネント
async または defer のない Twitter のフォローボタン
2 つのスクリプトを使用したデモ 1 のスクリプトとスクリプト コンポーネントの構成。

次の比較は、Next.js commerce スターター キットの両方のバージョンの進捗状況を視覚的に示したものです。ご覧のとおり、適切な読み込み戦略で Script コンポーネントを有効にすると、LCP は約 1 秒早く発生します。

LCP の改善を示すフィルムの比較

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

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

変更前 変更後
Google タグ マネージャーと async 4 つのスクリプトそれぞれに対して「Strategy = lazyonload」のスクリプト コンポーネント
非同期の Twitter のフォローボタン
非同期または遅延のない YouTube チャンネル登録ボタン
非同期または遅延のない LinkedIn のフォローボタン
4 つのスクリプトを使用したデモ 2 のスクリプトとスクリプト コンポーネントの構成。
スクリプト コンポーネントの有無にかかわらず、インデックス ページの読み込みの進行状況を示す動画。Script コンポーネントを使用した FCP が 0.5 秒改善されました。

動画で見たように、First Contentful Paint(FCP)は、スクリプト コンポーネントがないページでは 0.9 秒、スクリプト コンポーネントがあれば 0.4 秒で発生します。

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

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

ウェブワーカーの使用

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

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

CLS の最小化

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

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

ラッパー コンポーネント

Google アナリティクスや Google タグ マネージャー(GTM)などの一般的な第三者スクリプトを組み込む場合、構文や読み込み方法は固定されています。これらは、スクリプトのタイプごとに個別のラッパー コンポーネントにさらにカプセル化できます。デベロッパーが利用できるのは、アプリケーション固有の最小限の属性(トラッキング ID など)のみです。ラッパー コンポーネントは、デベロッパーにとって次のメリットをもたらします。

  1. 一般的なスクリプトタグを簡単に組み込めるようになります。
  2. フレームワークが内部で最も最適な戦略を使用するようにする。

まとめ

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

謝辞

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