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

Next.js の Script コンポーネントのビジョンについて学びます。このコンポーネントは、サードパーティ スクリプトの読み込みを最適化する組み込みソリューションを提供します。

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

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

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

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

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

Lighthouse 監査の「レンダリングを妨げるリソースの除外」と「サードパーティの使用の最小化」

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

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

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

Aurora の第三者スクリプトへの対応

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

フレームワークのパフォーマンスを改善するという目標を達成するための最も重要なステップの一つは、Next.js でサードパーティ スクリプトの理想的な読み込み順序を調査することでした。Next.js などのフレームワークは、デベロッパーがサードパーティを含むリソースを効率的に読み込むのに役立つ便利なデフォルトと機能を提供できるという独自の位置付けにあります。さまざまなフレームワークで最もレンダリングをブロックするサードパーティを特定するため、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 のチャンネル登録ボタン
非同期または遅延のない LinkedIn フォローボタン
スクリプト コンポーネントありとなしでの、インデックス ページの読み込みの進行状況を示す動画。スクリプト コンポーネントを使用すると、FCP が 0.5 秒改善されます。

動画でわかるように、First Contentful Paint(FCP)は、スクリプト コンポーネントのないページでは 0.9 秒、スクリプト コンポーネントがあるページでは 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 様、この投稿に関するフィードバックをお寄せいただきありがとうございました。