サードパーティの JavaScript を最適化する

サードパーティのスクリプトはパフォーマンスに影響するため、定期的に監査し、効率的に読み込む方法を使用することが重要です。この Codelab では、サードパーティ リソースの読み込みを最適化する方法について説明します。ここでは、次の方法について説明します。

  • スクリプトの読み込みを遅らせる

  • 重要でないリソースの遅延読み込み

  • 必要なオリジンに事前接続する

このサンプルアプリは、シンプルなウェブページを備えており、サードパーティのソースから 3 つの機能を利用できます。

  • 動画の埋め込み

  • 折れ線グラフをレンダリングするためのデータ可視化ライブラリ

  • ソーシャル メディアの共有ウィジェット

で確認できます。 <ph type="x-smartling-placeholder">
</ph> サードパーティのリソースがハイライト表示されたページのスクリーンショット。
サンプルアプリ内のサードパーティ リソース

まず、アプリのパフォーマンスを測定し、次にそれぞれの手法を適用して、アプリのパフォーマンスをさまざまな側面から改善します。

パフォーマンスの測定

まず、サンプルアプリを全画面で開きます。

  1. [Remix to Edit] をクリックして、プロジェクトを編集可能にします。
  2. サイトをプレビューするには、[アプリを表示] を押します。[ 全画面表示 全画面表示

ページに対して Lighthouse パフォーマンス監査を実行して、ベースライン パフォーマンスを確立します。

  1. Ctrl+Shift+J キー(Mac の場合は Command+Option+J キー)を押して DevTools を開きます。
  2. [Lighthouse] タブをクリックします。
  3. [モバイル] をクリックします。
  4. [パフォーマンス] チェックボックスをオンにします。([監査] セクションで、残りのチェックボックスはオフにできます)。
  5. [Simulated Fast 3G, 4x CPU Slowdown] をクリックします。
  6. [ストレージを消去] チェックボックスをオンにします。
  7. [Run audits] をクリックします。

マシンで監査を実行すると、正確な結果が異なる場合がありますが、First Contentful Paint(FCP)の時間はかなり長く、Lighthouse ではレンダリング ブロック リソースを排除する必要なオリジンに事前接続するという 2 つの方法で調査できます。(指標がすべて緑色になっている場合でも、最適化によって改善が見られます)。

2.4 秒の FCP と 2 つの機会(レンダリング ブロック リソースの排除)と、必要なオリジンへの事前接続を示す Lighthouse 監査のスクリーンショット。

サードパーティの JavaScript の読み込みを遅らせる

レンダリングをブロックするリソースを排除する」の監査で、d3js.org からのスクリプトを遅らせることで時間を節約できることが確認されました。

レンダリング ブロック リソースの監査を排除するスクリーンショット。d3.v3.min.js スクリプトがハイライト表示されている。

D3.js は、データを可視化するための JavaScript ライブラリです。サンプルアプリの script.js ファイルは、D3 ユーティリティ関数を使って SVG 折れ線グラフを作成し、ページに追加します。ここでのオペレーションの順序は重要です。script.js は、ドキュメントの解析と D3 ライブラリの読み込みが完了した後に実行する必要があります。そのため、index.html の終了 </body> タグの直前に含まれています。

ただし、D3 スクリプトはページの <head> に含まれているため、残りのドキュメントの解析はブロックされます。

見出しにスクリプトタグがハイライト表示された index.html のスクリーンショット。

スクリプトタグに以下の 2 つのマジック属性を追加すると、パーサーをブロック解除できます。

  • async を使用すると、スクリプトがバックグラウンドでダウンロードされ、ダウンロード完了後に最初のタイミングで実行されます。

  • defer は、スクリプトがバックグラウンドでダウンロードされ、解析が完全に終了した後に実行することを保証します。

このグラフはページ全体にとってそれほど重要ではなく、スクロールしなければ見えない範囲にある可能性が高いため、defer を使用してパーサーのブロックがないようにします。

ステップ 1: defer 属性を使用してスクリプトを非同期で読み込む

index.html の 17 行目で、<script> 要素に defer 属性を追加します。

<script src="https://d3js.org/d3.v3.min.js" defer></script>

ステップ 2: オペレーションが正しい順序であることを確認する

D3 が延期されるため、D3 の準備が整う前に script.js が実行され、エラーが発生します。

defer 属性を持つスクリプトは、指定された順序で実行されます。D3 の準備ができたら script.js が実行されるようにするには、defer を追加してドキュメントの <head> の D3 の <script> 要素の直後に移動します。これでパーサーがブロックされなくなり、ダウンロードが早く開始されます。

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

サードパーティ リソースを遅延読み込みする

スクロールしなければ見えない範囲にあるすべてのリソースは、遅延読み込みに適しています。

サンプルアプリでは、iframe に YouTube 動画が埋め込まれています。ページから行われたリクエストの数と、埋め込みの YouTube iframe からのリクエストを確認するには:

  1. サイトをプレビューするには、[アプリを表示] を押します。[ 全画面表示 全画面表示
  2. Ctrl+Shift+J キー(Mac の場合は Command+Option+J キー)を押して DevTools を開きます。
  3. [ネットワーク] タブをクリックします。
  4. [キャッシュを無効にする] チェックボックスをオンにします。
  5. [Throttling] プルダウン メニューで [Fast 3G] を選択します。
  6. ページを再読み込みする。

DevTools の [Network] パネルのスクリーンショット。

[Network] パネルを見ると、このページで合計 28 件のリクエストが行われ、約 1 MB の圧縮リソースが転送されたことがわかります。

YouTube iframe が行ったリクエストを特定するには、[Initiator] 列で動画 ID 6lfaiXM6waw を探します。すべてのリクエストをドメイン別にグループ化するには:

  • [ネットワーク] パネルで、列のタイトルを右クリックします。

  • プルダウン メニューで [ドメイン] 列を選択します。

  • ドメイン別にリクエストを並べ替えるには、[ドメイン] 列のタイトルをクリックします。

新しい並べ替えにより、Google ドメインに対する追加リクエストがあることがわかります。YouTube iframe は、スクリプト、スタイルシート、画像、フォントを合計 14 回リクエストします。ただし、ユーザーが実際に下にスクロールして動画を再生しない限り、そうしたアセットをすべて必要としているわけではありません。

ユーザーがページの該当セクションまで下にスクロールするまで動画が遅延読み込みされるのを待つことで、ページが最初に行うリクエスト数を減らすことができます。この方法により、初期読み込みを高速化できます

遅延読み込みを実装する 1 つの方法は、Intersection Observer を使用することです。これは、要素がブラウザのビューポートに出入りしたときに通知するブラウザ API です。

ステップ 1: 動画を最初に読み込まないようにする

動画の iframe を遅延読み込みするには、まず通常の方法で読み込まれないようにする必要があります。これを行うには、src 属性を data-src 属性に置き換えて、動画の URL を指定します。

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src は、標準の HTML 要素に関する追加情報を格納できるデータ属性です。データ属性には、「data-」で始まる任意の名前を付けることができます。

src のない iframe は読み込まれません。

ステップ 2: Intersection Observer を使用して動画を遅延読み込みする

ユーザーが動画をスクロールしたときに動画を読み込むには、それがいつ行われるかを把握する必要があります。そこで役立つのが Intersection Observer API です。Intersection Observer API を使用すると、追跡する要素がビューポートに出入りするたびに実行されるコールバック関数を登録できます。

まず、新しいファイルを作成して lazy-load.js という名前を付けます。

  • [New File] をクリックして名前を付けます。
  • [このファイルを追加] をクリックします。

ドキュメント ヘッドに次のスクリプトタグを追加します。

 <script src="/lazy-load.js" defer></script>

lazy-load.js で、新しい IntersectionObserver を作成し、実行するコールバック関数を渡します。

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

次に、observe メソッドに引数として渡して、observer に監視対象の要素(この場合は動画の iframe)を指定します。

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback は、IntersectionObserverEntry オブジェクトのリストと IntersectionObserver オブジェクト自体のリストを受け取ります。各エントリには、target 要素と、その寸法、位置、ビューポートに入った時刻などを記述するプロパティが含まれます。IntersectionObserverEntry のプロパティの一つに isIntersecting があります。これは、要素がビューポートに入るときに true と等しいブール値です。

この例では、targetiframe です。target がビューポートに入ると、isIntersectingtrue と等しくなります。実際の動作を確認するには、callback を次の関数に置き換えます。

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. サイトをプレビューするには、[アプリを表示] を押します。[ 全画面表示 全画面表示
  2. Ctrl+Shift+J キー(Mac の場合は Command+Option+J キー)を押して DevTools を開きます。
  3. [コンソール] タブをクリックします。

上下にスクロールしてみます。isIntersecting の値が変更され、ターゲット要素がコンソールに記録されます。

ユーザーが目的の位置にスクロールしたときに動画を読み込むには、loadElement 関数を実行する条件として isIntersecting を使用します。これにより、iframe 要素の data-src から値が取得され、iframe 要素の src 属性として設定されます。これにより、動画の読み込みがトリガーされます。次に、動画が読み込まれたら、observer に対して unobserve メソッドを呼び出して、ターゲット要素の監視を停止します。

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

ステップ 3: パフォーマンスを再評価する

リソースのサイズと数の変化を確認するには、DevTools の [Network] パネルを開き、ページを再度再読み込みします。[Network] パネルを見ると、そのページで 14 件のリクエストと 260 KB しか行われていないことがわかります。これは意味のある改善です。

ページを下にスクロールして、[ネットワーク] パネルを確認します。動画にアクセスすると、ページによって追加のリクエストがトリガーされていることがわかります。

必要な送信元に事前接続する

重要でない JavaScript を遅延させ、YouTube リクエストを遅延読み込みしました。次は、残りのサードパーティ コンテンツを最適化します。

リンクに rel=preconnect 属性を追加すると、そのリソースのリクエストが行われる前に、ドメインへの接続を確立するようブラウザに指示します。この属性は、ページが必要とすると確信できるリソースを提供するオリジンで使用するのに最適です。

最初のステップで実施した Lighthouse 監査は、必要なオリジンへの事前接続で提案されています。staticxx.facebook.com と youtube.com への早期の接続を確立することで約 400 ミリ秒節約できます。

必要なオリジンの監査に事前接続する。staticxx.facebook.com ドメインがハイライト表示されている。

今度は YouTube 動画が遅延読み込みされるため、ソーシャル メディア共有ウィジェットのソースである staticxx.facebook.com のみが残ります。このドメインへの早期接続は、ドキュメントの <head><link> タグを追加するだけで簡単に確立できます。

  <link rel="preconnect" href="https://staticxx.facebook.com">

パフォーマンスを再評価する

最適化後のページの状態は次のとおりです。Codelab のパフォーマンスを測定するセクションの手順に沿って、別の Lighthouse 監査を実行します。

1 秒の FCP とパフォーマンス スコア 99 を示す Lighthouse 監査。