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

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

Leena Sohoni
Leena Sohoni

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

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

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

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

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

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

重要なリソースが遅延せず、重要でないリソースが重要なリソースをブロックしないように、ページのリソース読み込みシーケンスを考慮することが重要です。

サードパーティの影響を軽減するためのベスト プラクティスはありますが、使用しているすべてのサードパーティにベスト プラクティスを実装する方法をすべてのユーザーが把握しているとは限りません。この方法は、次の理由から複雑になる可能性があります。

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

Aurora がサードパーティ スクリプトに重点を置く理由

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

フレームワークのパフォーマンスを向上させるという目標を達成するための最も重要なステップの 1 つは、Next.js でのサードパーティ スクリプトの理想的な読み込みシーケンスを調査することでした。Next.js などのフレームワークは、デベロッパーがサードパーティを含むリソースを効率的に読み込むのに役立つデフォルト設定や機能を提供するという点で、独自の地位を築いています。Google は、HTTP Archive と Lighthouse のデータを幅広く調査し、さまざまなフレームワークでレンダリングをブロックするサードパーティを特定しました。

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

フレームワーク コンポーネントを使用せずにサードパーティ スクリプトをシーケンス化する

レンダリングを妨げるスクリプトの影響を軽減するためのガイダンスでは、サードパーティ スクリプトを効率的に読み込んで順序付ける方法として、次のものが紹介されています。

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

       <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://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 の前に実行されるようにします。同意管理、bot 検出スクリプト、重要なコンテンツのレンダリングに必要なヘルパー ライブラリは、この戦略に適しています。

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

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

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

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

効果の測定

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

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

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

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

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

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

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

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

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

動画で確認できるように、First Contentful Paint(FCP)は、Script コンポーネントを使用しないページでは 0.9 秒、Script コンポーネントを使用するページでは 0.4 秒で発生します。

スクリプト コンポーネントの今後の展開

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

ウェブワーカーの使用

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

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

CLS を最小限に抑える

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

Script コンポーネントは、レイアウト シフトを引き起こす可能性のある埋め込みを読み込むために使用できます。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 に感謝します。