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

       <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 Script コンポーネント

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

Script コンポーネントは HTML <script> タグを基盤としており、strategy 属性を使用してサードパーティ スクリプトの読み込み優先度を設定できます。

// 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 属性には、次の 3 つの値を指定できます。

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

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

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

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

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

効果の測定

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

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

デモ用に、以下のサードパーティ スクリプトがコマース アプリ テンプレートに追加されています。

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

次の比較は、Next.js コマース スターターキットの両方のバージョンのビジュアル プログレスを示しています。ご覧のとおり、適切な読み込み戦略で Script コンポーネントを有効にすると、LCP がほぼ 1 秒早く発生します。

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

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

デモ ブログアプリには、次のようにサードパーティ スクリプトが追加されています。

4 つのスクリプトを使用したデモ 2 のスクリプトとスクリプト コンポーネントの構成。
変更後
Google タグ マネージャー(非同期) 4 つのスクリプトごとに、strategy = lazyonload のスクリプト コンポーネント
非同期の Twitter フォローボタン
YouTube のチャンネル登録ボタンに async または defer がない
LinkedIn のフォローボタンに async または defer がない
スクリプト コンポーネントありとなしでの、インデックス ページの読み込みの進行状況を示す動画。スクリプト コンポーネントを使用すると、FCP が 0.5 秒改善されます。

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

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

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

ウェブワーカーの使用

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

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

CLS の最小化

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

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

ラッパー コンポーネント

Google アナリティクスや Google タグ マネージャー(GTM)などの一般的なサードパーティ スクリプトを含める場合の構文と読み込み戦略は通常固定されています。これらは、スクリプト タイプごとに個別のラッパー コンポーネントにカプセル化できます。デベロッパーが利用できるアプリ固有の属性は、最小限のセットのみで(トラッキング ID など)。ラッパー コンポーネントは、以下の方法で開発者を支援します。

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

まとめ

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

謝辞

Kara Erickson 様、Janicklas Ralph 様、Katie Hempenius 様、Philip Walton 様、Jeremy Wagner 様、Addy Osmani 様、この投稿に関するフィードバックをお寄せいただきありがとうございました。