サードパーティ スクリプトはパフォーマンスに影響します。そのため、定期的に監査し、効率的な読み込み手法を使用することが重要です。この Codelab では、サードパーティ リソースの読み込みを最適化する方法について説明します。次の手法について説明します。
スクリプトの読み込みを遅らせる
重要でないリソースの遅延読み込み
必要なオリジンへの事前接続
含まれているサンプルアプリにはシンプルなウェブページがあり、サードパーティのソースから 3 つの機能を利用できます。
動画の埋め込み
折れ線グラフをレンダリングするデータ可視化ライブラリ
ソーシャル メディア共有ウィジェット
まず、アプリのパフォーマンスを測定し、次にそれぞれの手法を適用して、アプリのパフォーマンスをさまざまな側面から改善します。
パフォーマンスの測定
まず、サンプルアプリを全画面表示で開きます。
- [Remix to Edit] をクリックして、プロジェクトを編集可能にします。
- サイトをプレビューするには、[アプリを表示] を押してから、[全画面表示] を押します。
ページに対して Lighthouse パフォーマンス監査を実行して、ベースライン パフォーマンスを確立します。
- Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
- [Lighthouse] タブをクリックします。
- [モバイル] をクリックします。
- [パフォーマンス] チェックボックスをオンにします。([監査] セクションで残りのチェックボックスをオフにできます)。
- [高速 3G シミュレーション、CPU 4 倍減速] をクリックします。
- [ストレージを消去] チェックボックスをオンにします。
- [監査を実行] をクリックします。
マシンで監査を実行すると、正確な結果が異なる場合がありますが、First Contentful Paint(FCP)の時間はかなり長く、Lighthouse ではレンダリング ブロック リソースを排除する、必要なオリジンに事前接続するという 2 つの方法で調査できます。(指標がすべて緑色の場合でも、最適化によって改善が見込めます)。
サードパーティの JavaScript を遅らせる
「レンダリングをブロックするリソースを排除する」の監査で、d3js.org からのスクリプトを遅らせることで時間を節約できることが確認されました。
D3.js は、データ ビジュアリゼーションを作成するための JavaScript ライブラリです。サンプルアプリの script.js
ファイルでは、D3 ユーティリティ関数を使用して SVG 折れ線グラフを作成し、ページに追加しています。ここでは処理の順序が重要です。script.js
は、ドキュメントが解析され、D3 ライブラリが読み込まれた後に実行する必要があります。そのため、index.html
の終了タグ </body>
の直前に含まれています。
ただし、D3 スクリプトはページの <head>
に含まれているため、残りのドキュメントの解析がブロックされます。
スクリプトタグに以下の 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 から送信されるリクエストを確認するには:
- サイトをプレビューするには、[アプリを表示] を押してから、[全画面表示] を押します。
- Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
- [ネットワーク] タブをクリックします。
- [キャッシュを無効にする] チェックボックスをオンにします。
- [スロットリング] プルダウン メニューで [高速 3G] を選択します。
- ページを再読み込みする。
[Network] パネルを見ると、このページで合計 28 件のリクエストが行われ、約 1 MB の圧縮リソースが転送されたことがわかります。
YouTube iframe
が行ったリクエストを特定するには、[Initiator] 列で動画 ID 6lfaiXM6waw
を探します。すべてのリクエストをドメイン別にグループ化するには:
[ネットワーク] パネルで、列のタイトルを右クリックします。
プルダウン メニューで [Domains] 列を選択します。
リクエストをドメイン別に並べ替えるには、[ドメイン] 列のタイトルをクリックします。
新しい並べ替えでは、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] をクリックして名前を付けます。
- [Add This 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
のプロパティの 1 つが isIntersecting
です。これは、要素がビューポートに入ったときに true
に等しいブール値です。
この例では、target
は iframe
です。target
がビューポートに入ると、isIntersecting
は true
と等しくなります。実際の動作を確認するには、callback
を次の関数に置き換えます。
let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(entry => {
console.log(entry.target);
console.log(entry.isIntersecting);
});
});
- サイトをプレビューするには、[アプリを表示] を押してから、[全画面表示] を押します。
- Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
- [コンソール] タブをクリックします。
上下にスクロールしてみます。isIntersecting
の値が変更され、ターゲット要素がコンソールに記録されます。
ユーザーがスクロールして動画の位置に移動したときに動画を読み込むには、条件として isIntersecting
を使用して loadElement
関数を実行します。この関数は、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 の [ネットワーク] パネルを開き、ページをもう一度読み込みます。[ネットワーク] パネルには、ページが 14 件のリクエストを送信し、260 KB しか使用していないことが示されています。大きな改善です。
ページを下にスクロールし、[ネットワーク] パネルに注目します。動画にアクセスすると、ページで追加のリクエストがトリガーされます。
必須のドメインへの事前接続
重要でない JavaScript を遅らせ、YouTube リクエストを遅延読み込みしたので、残りのサードパーティ コンテンツを最適化する準備が整いました。
リンクに rel=preconnect
属性を追加すると、そのリソースのリクエストが行われる前に、ドメインへの接続を確立するようブラウザに指示します。この属性は、ページに必要なリソースを提供するオリジンに最適です。
最初のステップで実施した Lighthouse 監査は、必要なオリジンへの事前接続で提案されています。staticxx.facebook.com と youtube.com への早期接続を確立することで約 400 ミリ秒節約できます。
YouTube 動画が遅延読み込みされるようになったため、残るのは、ソーシャル メディア共有ウィジェットのソースである staticxx.facebook.com のみです。このドメインへの早期接続は、ドキュメントの <head>
に <link>
タグを追加するだけで簡単に確立できます。
<link rel="preconnect" href="https://staticxx.facebook.com">
パフォーマンスを再評価する
最適化後のページの状態は次のとおりです。Codelab のパフォーマンスを測定するセクションの手順に沿って、別の Lighthouse 監査を実行します。