フロントエンドパフォーマンス2021:配信の最適化

公開: 2022-03-10
簡単なまとめ↬2021を…速くしましょう! メトリックからツール、フロントエンドテクニックまで、今日Webで高速なエクスペリエンスを作成するために知っておく必要のあるすべてを含む、毎年のフロントエンドパフォーマンスチェックリスト。 2016年から更新。

目次

  1. 準備:計画と指標
  2. 現実的な目標の設定
  3. 環境の定義
  4. 資産の最適化
  5. ビルドの最適化
  6. 配信の最適化
  7. ネットワーキング、HTTP / 2、HTTP / 3
  8. テストとモニタリング
  9. クイックウィン
  10. 1ページのすべて
  11. チェックリストをダウンロードする(PDF、Apple Pages、MS Word)
  12. 次のガイドを見逃さないように、メールマガジンを購読してください。

配信の最適化

  1. 重要なJavaScriptを非同期でロードするためにdeferを使用しますか?
    ユーザーがページをリクエストすると、ブラウザはHTMLをフェッチしてDOMを構築し、次にCSSをフェッチしてCSSOMを構築し、DOMとCSSOMを照合してレンダリングツリーを生成します。 JavaScriptを解決する必要がある場合、ブラウザは解決されるまでページのレンダリングを開始しないため、レンダリングが遅れます。 開発者として、ブラウザに待機せず、ページのレンダリングを開始するように明示的に指示する必要があります。 スクリプトに対してこれを行う方法は、HTMLのdefer属性とasync属性を使用することです。

    実際には、 asyncの代わりにdeferを使用する方が良いことがわかります。 ああ、また違いは何ですか? Steve Soudersによると、 asyncスクリプトが到着すると、スクリプトの準備ができるとすぐに実行されます。 これが非常に高速に発生する場合、たとえば、スクリプトがキャッシュリーダーにある場合、実際にはHTMLパーサーをブロックできます。 deferを使用すると、ブラウザはHTMLが解析されるまでスクリプトを実行しません。 したがって、レンダリングを開始する前にJavaScriptを実行する必要がない限り、 deferを使用することをお勧めします。 また、複数の非同期ファイルは非決定論的な順序で実行されます。

    asyncdeferについては、いくつかの誤解があることに注意してください。 最も重要なことは、 asyncは、スクリプトの準備ができたときにコードが実行されることを意味するわけではありません。 これは、スクリプトの準備が整い先行するすべての同期作業が完了するたびに実行されることを意味します。 ハリー・ロバーツの言葉によれば、「同期スクリプトの後にasyncスクリプトを配置すると、 asyncスクリプトは最も遅い同期スクリプトと同じくらい速くなります。」

    また、 asyncdeferの両方を使用することはお勧めしません。 最新のブラウザは両方をサポートしていますが、両方の属性が使用されている場合は常に、 asyncが優先されます。

    詳細を詳しく知りたい場合は、Milica Mihajlijaが、投機的構文解析、非同期、および延期の詳細について、DOMの高速構築に関する非常に詳細なガイドを作成しました。

  2. IntersectionObserverと優先度のヒントを使用して、高価なコンポーネントを遅延ロードします。
    一般に、重いJavaScript、ビデオ、iframe、ウィジェット、場合によっては画像など、高価なコンポーネントをすべて遅延読み込みすることをお勧めします。 ネイティブの遅延読み込みは、 loading属性を持つ画像とiframeですでに利用可能です(Chromiumのみ)。 内部的には、この属性は、ビューポートから計算された距離に達するまで、リソースのロードを延期します。
    <!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />

    そのしきい値は、フェッチされる画像リソースのタイプから有効な接続タイプまで、いくつかの要因に依存します。 ただし、AndroidでChromeを使用して実施された実験によると、4Gでは、遅延読み込みされた折り畳み下の画像の97.5%が、表示されてから10ミリ秒以内に完全に読み込まれたため、安全であるはずです。

    <script><img> 、または<link>要素でimportance属性( highまたはlow )を使用することもできます(点滅のみ)。 実際、カルーセル内の画像の優先順位を下げたり、スクリプトの優先順位を付け直したりするのに最適な方法です。 ただし、場合によっては、もう少しきめ細かい制御が必要になることがあります。

    <!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />

    少し洗練された遅延読み込みを行う最もパフォーマンスの高い方法は、Intersection Observer APIを使用することです。これは、ターゲット要素と祖先要素またはトップレベルドキュメントのビューポートとの交差の変化を非同期的に監視する方法を提供します。 基本的に、コールバック関数と一連のオプションを受け取る新しいIntersectionObserverオブジェクトを作成する必要があります。 次に、観察するターゲットを追加します。

    コールバック関数は、ターゲットが表示または非表示になったときに実行されるため、ビューポートをインターセプトすると、要素が表示される前にいくつかのアクションを開始できます。 実際、 rootMargin (ルートの周囲のマージン)とthreshold (ターゲットの可視性の何パーセントを目指しているかを示す単一の数値または数値の配列)を使用して、オブザーバーのコールバックを呼び出すタイミングをきめ細かく制御できます。

    Alejandro Garcia Angladaは、実際に実装する方法に関する便利なチュートリアルを公開しています。RahulNanwaniは、遅延読み込みの前景と背景の画像に関する詳細な投稿を作成しました。GoogleFundamentalsは、交差点オブザーバーを使用した遅延読み込みの画像と動画に関する詳細なチュートリアルも提供しています。

    アート指向のストーリーテリングで、動くオブジェクトや粘着性のあるオブジェクトを使った長い読み物を覚えていますか? IntersectionObserverを使用してパフォーマンスの高いスクロールテリングを実装することもできます。

    他に遅延読み込みできるものをもう一度確認してください。 遅延読み込みの翻訳文字列や絵文字でさえ役立つ可能性があります。 そうすることで、モバイルTwitterは、新しい国際化パイプラインからJavaScriptの実行を80%高速化することができました。

    ただし、簡単に注意してください。遅延読み込みは、ルールではなく例外である必要があることに注意してください。 製品ページの画像、ヒーロー画像、メインナビゲーションをインタラクティブにするために必要なスクリプトなど、実際に人々にすばやく見てもらいたいものを遅延読み込みするのはおそらく合理的ではありません。

160KBのダウンロードで3000pxの古いしきい値を示す例(左)、新しいしきい値の量は1250pxで、ダウンロードは90KBのみ(右)で、imgの読み込みの遅延データの節約が改善されていることを示しています。
高速接続(例:4G)では、Chromeのビューポートからの距離のしきい値が最近3000pxから1250pxに減少し、低速接続(例:3G)では、しきい値が4000pxから2500pxに変更されました。 (大プレビュー)
Twitter UIが表示された携帯電話の周りにテキストが表示されたイラストで、遅延読み込みの翻訳文字列によるツールの改善について説明しています。
モバイルTwitterは、翻訳文字列を遅延読み込みすることで、新しい国際化パイプラインからJavaScriptの実行を80%高速化することに成功しました。 (画像クレジット:Addy Osmani)(大プレビュー)
  1. 画像を段階的にロードします。
    ページにプログレッシブ画像の読み込みを追加することで、遅延読み込みを次のレベルに引き上げることもできます。 Facebook、Pinterest、Medium、Woltと同様に、最初に低品質またはぼやけた画像を読み込んでから、ページの読み込みを続けながら、BlurHashテクニックまたはLQIP(低品質画像プレースホルダー)を使用してフル品質バージョンに置き換えます。技術。

    これらの手法によってユーザーエクスペリエンスが向上するかどうかについては意見が異なりますが、First ContentfulPaintまでの時間は確実に向上します。 低品質バージョンの画像をSVGプレースホルダーとして作成するSQIP、またはCSS線形グラデーションを使用したグラデーション画像プレースホルダーを使用して自動化することもできます。

    これらのプレースホルダーは、テキスト圧縮メソッドで自然に圧縮されるため、HTML内に埋め込むことができます。 Dean Humeは、彼の記事で、IntersectionObserverを使用してこの手法を実装する方法について説明しています。

    後退する? ブラウザが交差点オブザーバーをサポートしていない場合でも、ポリフィルを遅延ロードするか、画像をすぐにロードできます。 そして、そのためのライブラリもあります。

    もっと空想に行きたいですか? 画像をトレースし、プリミティブシェイプとエッジを使用して、軽量のSVGプレースホルダーを作成し、最初にロードしてから、プレースホルダーベクトルイメージから(ロードされた)ビットマップイメージに遷移することができます。

  2. Jose M. PerezによるSVG遅延読み込み手法を示す3つの異なるバージョン、左側にキュービズムアートに類似したバージョン、中央にピクセル化されたぼやけたバージョン、右側にJose自身の適切な写真
    Jose M.PerezによるSVG遅延読み込み手法。 (大プレビュー)
  3. content-visibilityでレンダリングを延期しますか?
    豊富なコンテンツブロック、画像、ビデオを含む複雑なレイアウトの場合、データのデコードとピクセルのレンダリングは、特にローエンドデバイスでは、非常にコストのかかる操作になる可能性があります。 content-visibility: autoを使用すると、コンテナーがビューポートの外にある間、ブラウザーに子のレイアウトをスキップするように求めることができます。

    たとえば、初期ロード時にフッターと後期セクションのレンダリングをスキップできます。

    footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }

    content-visibility:auto;に注意してください。 オーバーフローのように動作します:非表示。 、ただし、デフォルトのmargin-leftの代わりにpadding padding-leftpadding-rightを適用することで修正できますmargin-left: auto;margin-right: auto; と宣言された幅。 パディングを使用すると、基本的に、要素がコンテンツボックスをオーバーフローして、ボックスモデル全体を離れたり、切り取られたりすることなく、パディングボックスに入ることができます。

    また、新しいコンテンツが最終的にレンダリングされるときにCLSが導入される可能性があることに注意してください。そのため、適切なサイズのプレースホルダーを使用しcontain-intrinsic-sizeを使用することをお勧めします(ありがとう、Una! )。

    Thijs Terluinには、両方のプロパティと、ブラウザによるcontain-intrinsic-sizeの計算方法に関する詳細があり、Malte Ublはそれを計算する方法を示し、JakeとSurmaによる簡単なビデオ説明者がすべての動作を説明しています。

    また、CSS Containmentを使用してもう少し細かくする必要がある場合は、他の要素のサイズ、配置、または計算されたスタイルのみが必要な場合、または要素が現在ある場合は、DOMノードの子孫のレイアウト、スタイル、およびペイント作業を手動でスキップできます。オフキャンバス。

初期ロードでのレンダリングパフォーマンスは、ベースライン(左)で2,288ミリ秒、content-visibility:auto(右)のチャンクで13,464ミリ秒です。
デモでは、 content-visibility: autoをチャンク化されたコンテンツ領域に適用すると、初期ロード時にレンダリングパフォーマンスが7倍向上します。 (大プレビュー)
  1. decode decoding="async"を延期しますか?
    コンテンツが画面外に表示されることもありますが、お客様が必要なときにコンテンツを利用できるようにする必要があります。理想的には、クリティカルパスで何もブロックせず、非同期でデコードおよびレンダリングします。 decode decoding="async"を使用して、メインスレッドから画像をデコードする許可をブラウザに与えることができます。これにより、(Malte Ublを介して)画像のデコードに使用されるCPU時間のユーザーへの影響を回避できます。

    <img decoding="async" … />

    または、オフスクリーン画像の場合、最初にプレースホルダーを表示し、画像がビューポート内にあるときに、IntersectionObserverを使用して、画像をバックグラウンドでダウンロードするためのネットワーク呼び出しをトリガーできます。 また、img.decode()でデコードするまでレンダリングを延期するか、Image DecodeAPIが利用できない場合は画像をダウンロードすることができます。

    画像をレンダリングするとき、たとえばフェードインアニメーションを使用できます。 KatieHempeniusとAddyOsmaniは、彼らの講演「Speed at Scale:Web Performance Tips and Tricks fromtheTrenches」でより多くの洞察を共有しています。

  2. 重要なCSSを生成して提供していますか?
    ブラウザができるだけ早くページのレンダリングを開始できるようにするために、ページの最初の表示部分のレンダリングを開始するために必要なすべてのCSS(「クリティカルCSS」または「オーバーザフォールドCSS」と呼ばれる)を収集することが一般的な方法になりました。 ")そしてそれをページの<head>にインラインで含めることで、ラウンドトリップを減らします。 スロースタートフェーズ中に交換されるパッケージのサイズが限られているため、重要なCSSの予算は約14KBです。

    それを超えると、ブラウザはより多くのスタイルをフェッチするために追加のラウンドトリップが必要になります。 CriticalCSSとCriticalを使用すると、使用しているすべてのテンプレートの重要なCSSを出力できます。 しかし、私たちの経験では、すべてのテンプレートの重要なCSSを手動で収集するよりも優れた自動システムはありません。実際、これが最近戻ってきたアプローチです。

    次に、重要なCSSをインライン化し、残りをcrittersWebpackプラグインで遅延ロードできます。 可能であれば、フィラメントグループで使用される条件付きインライン化アプローチを使用するか、インラインコードをその場で静的アセットに変換することを検討してください。

    現在、loadCSSなどのライブラリを使用して完全なCSSを非同期でロードしている場合、実際には必要ありません。 media="print"を使用すると、ブラウザをだましてCSSを非同期にフェッチさせることができますが、ロードされると画面環境に適用されます。 (ありがとう、スコット!

    <!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />

    各テンプレートのすべての重要なCSSを収集するときは、「折り畳みの上」の領域だけを探索するのが一般的です。 ただし、複雑なレイアウトの場合は、レイアウトの基礎も含めて、大量の再計算と再描画のコストを回避し、結果としてCore WebVitalsスコアを損なうことを回避することをお勧めします。

    ユーザーがページの中央に直接リンクしているURLを取得したが、CSSがまだダウンロードされていない場合はどうなりますか? その場合、重要ではないコンテンツを非表示にすることが一般的になりました。たとえば、 opacity: 0; インラインCSSおよびopacity: 1 、CSSが使用可能な場合に表示します。 ただし、接続速度が遅いユーザーはページのコンテンツを読み取ることができない可能性があるため、大きな欠点があります。 そのため、適切なスタイルが設定されていない場合でも、コンテンツは常に表示されたままにしておくことをお勧めします。

    重要なCSS(およびその他の重要なアセット)をルートドメインの別のファイルに配置すると、キャッシュが原因で、インライン化よりも多くの利点があります。 Chromeは、ページをリクエストするときにルートドメインへの2番目のHTTP接続を投機的に開きます。これにより、このCSSをフェッチするためのTCP接続が不要になります。 つまり、重要な-CSSファイルのセット(たとえば、 critical-homepage.csscritical-product-page.cssなど)を作成し、それらをインライン化することなく、ルートから提供できます。 (ありがとう、フィリップ!

    注意:HTTP / 2を使用すると、重要なCSSを別のCSSファイルに保存し、HTMLを肥大化させることなくサーバープッシュを介して配信できます。 キャッチは、サーバーのプッシュがブラウザ間での多くの落とし穴と競合状態で厄介だったことです。 一貫してサポートされることはなく、キャッシュの問題がいくつかありました(Hooman Beheshtiのプレゼンテーションのスライド114以降を参照)。

    実際、この影響はマイナスになり、ネットワークバッファが肥大化し、ドキュメント内の本物のフレームが配信されなくなる可能性があります。 そのため、当面の間、Chromeがサーバープッシュのサポートを削除することを計画していることはそれほど驚くことではありませんでした。

  3. CSSルールを再グループ化してみてください。
    重要なCSSには慣れていますが、それを超える可能性のある最適化がいくつかあります。 ハリー・ロバーツは驚くべき研究を行い、驚くべき結果をもたらしました。 たとえば、メインのCSSファイルを個々のメディアクエリに分割することをお勧めします。 このようにして、ブラウザは優先度の高い重要なCSSを取得し、優先度の低い他のすべてのものを取得します—完全にクリティカルパスから外れます。

    また、 asyncスニペットの前に<link rel="stylesheet" />を配置することは避けてください。 スクリプトがスタイルシートに依存しない場合は、ブロックするスタイルの上にブロックするスクリプトを配置することを検討してください。 その場合は、そのJavaScriptを2つに分割し、CSSのいずれかの側にロードします。

    Scott Jehlは、インラインCSSファイルをService Workerでキャッシュすることで、別の興味深い問題を解決しました。これは、重要なCSSを使用している場合によくある問題です。 基本的に、JavaScriptを使用して簡単に見つけられるようにstyle要素にID属性を追加します。次に、JavaScriptの小さな部分がそのCSSを見つけ、Cache APIを使用してローカルブラウザキャッシュに保存します(コンテンツタイプはtext/css )。 text/css )以降のページで使用します。 後続のページにインライン化することを避け、代わりにキャッシュされたアセットを外部から参照するために、サイトへの最初のアクセス時にCookieを設定します。 出来上がり!

    動的なスタイリングも高価になる可能性があることに注意してください。ただし、通常は、同時にレンダリングされる何百もの構成されたコンポーネントに依存している場合に限ります。 したがって、CSS-in-JSを使用している場合は、CSSがテーマや小道具に依存していないときにCSS-in-JSライブラリが実行を最適化し、スタイル付きコンポーネントを過剰に構成しないようにしてください。 Aggelos Arvanitakisは、CSS-in-JSのパフォーマンスコストに関するより多くの洞察を共有しています。

  4. 応答をストリーミングしますか?
    忘れられたり無視されたりすることが多いストリームは、データの非同期チャンクを読み書きするためのインターフェイスを提供します。そのサブセットのみが、いつでもメモリで使用できる可能性があります。 基本的に、元のリクエストを行ったページは、データの最初のチャンクが利用可能になるとすぐにレスポンスの処理を開始でき、ストリーミング用に最適化されたパーサーを使用してコンテンツを段階的に表示できます。

    複数のソースから1つのストリームを作成できます。 たとえば、空のUIシェルを提供してJavaScriptにデータを入力させる代わりに、シェルがキャッシュから取得され、本体がネットワークから取得されるストリームをServiceWorkerに構築させることができます。 Jeff Posnickが指摘したように、Webアプリが部分的なテンプレートをつなぎ合わせてHTMLをサーバーレンダリングするCMSを利用している場合、そのモデルは、サーバーではなくService Workerで複製されたテンプレートロジックを使用して、ストリーミング応答を使用するように直接変換されます。 JakeArchibaldのTheYear of Web Streamsの記事は、それをどのように正確に構築できるかを強調しています。 パフォーマンスの向上は非常に顕著です。

    HTML応答全体をストリーミングすることの重要な利点の1つは、最初のナビゲーション要求中にレンダリングされたHTMLが、ブラウザーのストリーミングHTMLパーサーを最大限に活用できることです。 ページの読み込み後にドキュメントに挿入されるHTMLのチャンク(JavaScriptを介して入力されたコンテンツで一般的)は、この最適化を利用できません。

    ブラウザのサポート? Chrome、Firefox、Safari、Edgeで部分的にサポートされており、最新のすべてのブラウザーでサポートされているAPIとServiceWorkerをサポートしています。 また、冒険心があれば、ストリーミングリクエストの実験的な実装を確認できます。これにより、本文を生成しながらリクエストの送信を開始できます。 Chrome85で利用できます。

Android Chromeでのデータ保存の使用状況と、2019年11月と2020年4月にCloudinaryの調査で発見された平均imgヒットまたはセッションをまとめた画像
Cloudinaryの調査によると、世界中のAndroid Chromeユーザーの18%がLiteモード(別名Save-Data)を有効にしています。 (大プレビュー)
  1. コンポーネントを接続対応にすることを検討してください。
    データは高額になる可能性があり、ペイロードが増えるにつれて、サイトやアプリにアクセスする際にデータの節約を選択するユーザーを尊重する必要があります。 Save-Dataクライアントヒントリクエストヘッダーを使用すると、コストとパフォーマンスに制約のあるユーザー向けにアプリケーションとペイロードをカスタマイズできます。

    実際、高DPI画像のリクエストを低DPI画像に書き換えたり、ウェブフォント、派手な視差効果を削除したり、サムネイルと無限スクロールをプレビューしたり、動画の自動再生をオフにしたり、サーバーをプッシュしたり、表示されるアイテムの数を減らしたり、画質をダウングレードしたりできます。マークアップの配信方法も変更します。 Tim Vereeckeは、データ保存の多くのオプションを特徴とするdata-s(h)aver戦略に関する非常に詳細な記事を公開しました。

    誰がsave-dataを使用していますか、あなたは疑問に思うかもしれませんか? 世界のAndroidChromeユーザーの18%がライトモードを有効にしており( Save-Dataオン)、その数はもっと多くなる可能性があります。 Simon Hearneの調査によると、オプトイン率は安価なデバイスで最も高くなりますが、外れ値はたくさんあります。 例:カナダのユーザーのオプトイン率は34%を超え(米国の約7%と比較して)、最新のSamsungフラッグシップのユーザーのオプトイン率は世界全体でほぼ18%です。

    [ Save-Data ]モードをオンにすると、Chrome Mobileは最適化されたエクスペリエンスを提供します。つまり、遅延スクリプト、強制されたfont-display: swap 、および強制された遅延読み込みを使用したプロキシWebエクスペリエンスを提供します。 これらの最適化を行うためにブラウザに依存するよりも、自分でエクスペリエンスを構築する方が賢明です。

    ヘッダーは現在、Chromium、AndroidバージョンのChrome、またはデスクトップデバイスのデータセーバー拡張機能でのみサポートされています。 最後に、Network Information APIを使用して、ネットワークタイプに基づいて、コストのかかるJavaScriptモジュール、高解像度の画像およびビデオを配信することもできます。 ネットワーク情報API、特にnavigator.connection.effectiveTypeは、 RTTdownlinkeffectiveType値(およびその他のいくつか)を使用して、ユーザーが処理できる接続とデータの表現を提供します。

    このコンテキストでは、Max Bockは接続対応コンポーネントについて話し、AddyOsmaniは適応モジュールサービングについて話します。 たとえば、Reactを使用すると、接続タイプごとに異なるレンダリングを行うコンポーネントを作成できます。 Maxが示唆したように、ニュース記事の<Media />コンポーネントは次のように出力する可能性があります。

    • Offlinealtテキストを含むプレースホルダー、
    • 2G / save-dataモード:低解像度画像、
    • 非Retina画面での3G :中解像度の画像、
    • Retina画面の3G :高解像度のRetina画像、
    • 4G :HDビデオ。

    Dean Humeは、ServiceWorkerを使用して同様のロジックの実用的な実装を提供します。 ビデオの場合、デフォルトでビデオポスターを表示してから、「再生」アイコン、ビデオプレーヤーシェル、ビデオのメタデータなどをより適切な接続で表示できます。 サポートされていないブラウザのフォールバックとして、 canplaythroughイベントをリッスンし、 canplaythroughイベントが2秒以内に発生しない場合は、 Promise.race()を使用してソースの読み込みをタイムアウトすることができます。

    もう少し深く掘り下げたい場合は、開始するためのリソースがいくつかあります。

    • Addy Osmaniは、Reactでアダプティブサービングを実装する方法を示しています。
    • React Adaptive Loading Hooks&Utilitiesは、Reactのコードスニペットを提供します。
    • Netanel Baselは、Angularの接続対応コンポーネントを調査します。
    • Theodore Vorilasが、Vueでネットワーク情報APIを使用してアダプティブコンポーネントを提供する方法について説明します。
    • Umar Hansaは、高価なJavaScriptを選択的にダウンロード/実行する方法を示しています。
  2. コンポーネントをデバイスメモリ対応にすることを検討してください。
    ただし、ネットワーク接続では、ユーザーのコンテキストで1つの視点しか得られません。 さらに、Device Memory APIを使用して、使用可能なデバイスメモリに基づいてリソースを動的に調整することもできます。 navigator.deviceMemoryは、デバイスに搭載されているRAMの量をギガバイト単位で返し、最も近い2の累乗に切り捨てられます。 APIは、同じ値を報告するクライアントヒントヘッダーであるDevice-Memoryも備えています。

    ボーナスUmar Hansaは、動的インポートを使用して高価なスクリプトを延期し、デバイスメモリ、ネットワーク接続、およびハードウェアの同時実行性に基づいてエクスペリエンスを変更する方法を示しています。

Chrome46以降のBlinkでさまざまなリソースがどのように優先されているかを示す内訳
Chrome46以降のBlinkでさまざまなリソースがどのように優先されているかを示す内訳。 (画像クレジット:Addy Osmani)(大プレビュー)
  1. 接続をウォームアップして配信を高速化します。
    リソースヒントを使用して、 dns-prefetch (バックグラウンドでDNSルックアップを実行する)、 preconnect (ブラウザーにバックグラウンドで接続ハンドシェイク(DNS、TCP、TLS)を開始するように要求する)、 prefetch (ブラウザーに要求する)の時間を節約しますリソースを要求するため)およびpreload (特に、リソースを実行せずにプリフェッチします)。 最新のブラウザで十分にサポートされており、Firefoxでもまもなくサポートされる予定です。

    prerender覚えていますか? 次のナビゲーションのためにバックグラウンドでページ全体を構築するようにブラウザに促すために使用されるリソースヒント。 実装の問題は非常に問題があり、膨大なメモリフットプリントや帯域幅の使用から、複数の登録された分析ヒットや広告の表示回数にまで及びました。

    当然のことながら、これは非推奨になりましたが、ChromeチームはNoStateプリフェッチメカニズムとして復活させました。 実際、Chromeは事前レンダリングヒントを代わりにprerenderプリフェッチとして扱うため、今日でも使用できます。 Katie Hempeniusがその記事で説明しているように、「事前レンダリングと同様に、 NoState Prefetchは事前にリソースをフェッチしますが、事前レンダリングとは異なり、 JavaScriptを実行したり、ページの一部を事前にレンダリングしたりすることはありません。」

    NoStateプリフェッチは最大45MiBのメモリのみを使用し、フェッチされるサブリソースはIDLEネットプライオリティでフェッチされます。 Chrome 69以降、 NoState Prefetchは、通常のブラウジングと区別できるようにするために、すべてのリクエストにヘッダープリフェッチを追加します。

    また、プライバシーを意識した事前レンダリングに向けた新たな取り組みである事前レンダリングの選択肢とポータルにも注意してください。これにより、シームレスなナビゲーションのためにコンテンツのはめ込みpreviewが提供されます。

    リソースヒントを使用することは、おそらくパフォーマンスを向上させる最も簡単な方法であり、実際にうまく機能します。 いつ何を使うの? Addy Osmaniが説明したように、現在のページや、ユーザーがまだアクセスしていないページに必要なWebpackバンドルなど、複数のナビゲーション境界を越えた将来のナビゲーションに使用される可能性が非常に高いことがわかっているリソースをプリロードすることは合理的です。

    Addyの「Chromeでの優先度の読み込み」に関する記事では、Chromeがリソースヒントを正確に解釈する方法を示しています。したがって、レンダリングに重要なアセットを決定したら、それらに高い優先度を割り当てることができます。 リクエストがどのように優先されるかを確認するには、Chrome DevToolsネットワークリクエストテーブル(およびSafari)で「優先度」列を有効にします。

    最近のほとんどの場合、少なくともpreconnectdns-prefetchを使用し、 prefetchpreloadprerenderの使用には注意が必要です。 preconnectdns-prefetchを使用しても、ブラウザには並行して検索/接続するホストの数に制限があるため、優先度に基づいて注文するのが安全です( PhilipTellisに感謝します)。

    フォントは通常、ページ上の重要なアセットであるため、 preloadを使用して重要なフォントをダウンロードするようにブラウザに要求することをお勧めします。 ただし、フォントをプリロードするときに優先順位のパズルがあるため、実際にパフォーマンスに役立つかどうかを再確認してください。 preloadは非常に重要であると見なされているため、重要なCSSなどのさらに重要なリソースを飛躍させることができます。 (ありがとう、バリー!

    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
    <!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />

    <link rel="preload">media属性を受け入れるため、上記のように、 @mediaクエリルールに基づいてリソースを選択的にダウンロードすることを選択できます。

    さらに、 imagesizes imagesrcsetを使用して、最近発見されたヒーロー画像や、JavaScriptを介して読み込まれる画像(映画のポスターなど)をより速くプリロードできます。

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>

    JSONをfetchとしてプリロードすることもできるため、JavaScriptがJSONを要求する前にJSONが検出されます。

    <!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>

    スクリプトを怠惰に実行するために、JavaScriptを動的に効果的にロードすることもできます。

    /* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);

    覚えておくべきいくつかの落とし穴: preloadは、アセットのダウンロード開始時間を最初のリクエストに近づけるのに適していますが、プリロードされたアセットは、リクエストを行うページに関連付けられているメモリキャッシュに格納されます。 preloadはHTTPキャッシュでうまく機能します。アイテムがすでにHTTPキャッシュにある場合、ネットワーク要求は送信されません。

    したがって、最近発見されたリソース、 background-imageを介して読み込まれたヒーロー画像、重要なCSS(またはJavaScript)のインライン化、残りのCSS(またはJavaScript)の事前読み込みに役立ちます。

    トムハンクス主演のグレイハウンド映画の表紙を使用して、JavaScriptで検出するのを待つ必要がないため、プリロードされた画像が早く読み込まれることを示す例
    重要な画像を早期にプリロードします。 それらを発見するためにJavaScriptを待つ必要はありません。 (画像クレジット:AddyOsmaniによる「PreloadLate-DiscoveredHero Images Faster」)(大プレビュー)

    preloadタグは、ブラウザがサーバーからHTMLを受信し、先読みパーサーがpreloadタグを検出した後でのみ、プリロードを開始できます。 ブラウザがHTMLを解析してリクエストを開始するのを待たないため、HTTPヘッダーを介したプリロードは少し速くなる可能性があります(ただし、議論されています)。

    初期のヒントはさらに役立ち、HTMLの応答ヘッダーが送信される前でもプリロードを開始できるようにします(Chromium、Firefoxのロードマップ上)。 さらに、優先度のヒントは、スクリプトの読み込みの優先度を示すのに役立ちます。

    注意preloadを使用している場合は、定義as必要があるか、何もロードしない場合に加えて、 crossorigin属性のないプリロードされたフォントはダブルフェッチされます。 prefetchを使用している場合は、FirefoxのAgeヘッダーの問題に注意してください。

所定の期間(ミリ秒単位)でカウントが0から150の最初の満足のいくペイント(サーバーワーカーのステータス別)を示すグラフ
Service Workerを使用すると、最小限のデータを要求し、そのデータを完全なHTMLドキュメントに変換してFCPを向上させることができます。 (Phil Walton経由)(大プレビュー)
  1. キャッシングとネットワークフォールバックにサービスワーカーを使用します。
    ネットワーク上でのパフォーマンスの最適化は、ユーザーのマシンにローカルに保存されたキャッシュよりも高速になることはありません(ただし例外があります)。 WebサイトがHTTPSで実行されている場合、静的アセットをService Workerキャッシュにキャッシュし、オフラインフォールバック(またはオフラインページ)を保存して、ネットワークにアクセスするのではなく、ユーザーのマシンから取得できます。

    Phil Waltonが提案したように、Service Workerを使用すると、プログラムで応答を生成することで、より小さなHTMLペイロードを送信できます。 サービスワーカーは、サーバーに必要な最小限のデータ(たとえば、HTMLコンテンツの部分、Markdownファイル、JSONデータなど)を要求し、プログラムでそのデータを完全なHTMLドキュメントに変換できます。 したがって、ユーザーがサイトにアクセスしてService Workerがインストールされると、ユーザーは完全なHTMLページを再度要求することはありません。 パフォーマンスへの影響は非常に印象的です。

    ブラウザのサポート? サービスワーカーは広くサポートされており、フォールバックはとにかくネットワークです。 パフォーマンスの向上に役立ちますか? そうそう、そうです。 また、Background Fetchを使用すると、ServiceWorkerを介したバックグラウンドのアップロード/ダウンロードも可能になります。

    サービスワーカーのユースケースは多数あります。 たとえば、「オフライン用に保存」機能を実装したり、壊れた画像を処理したり、タブ間にメッセージングを導入したり、リクエストの種類に基づいてさまざまなキャッシュ戦略を提供したりできます。 一般に、一般的な信頼できる戦略は、アプリシェルを、オフラインページ、フロントページなど、ケースで重要になる可能性のあるいくつかの重要なページとともに、ServiceWorkerのキャッシュに保存することです。

    ただし、覚えておくべきいくつかの落とし穴があります。 サービスワーカーが配置されている場合、Safariでの範囲リクエストに注意する必要があります(サービスワーカーにWorkboxを使用している場合は、範囲リクエストモジュールがあります)。 DOMException: Quota exceeded. ブラウザコンソールでエラーが発生した場合は、Gerardoの記事「7KBが7MBに等しい場合」を調べてください。

    Gerardoは次のように述べています。「プログレッシブウェブアプリを構築していて、サービスワーカーがCDNから提供される静的アセットをキャッシュするときにキャッシュストレージが肥大化する場合は、クロスオリジンリソースに適切なCORS応答ヘッダーが存在することを確認してください。不透明な応答はキャッシュしないでください。サービスワーカーが意図せずに、クロスcrossorigin属性を<img>タグに追加することで、クロスオリジンイメージアセットをCORSモードにオプトインします。」

    サービスワーカーを始めるための優れたリソースはたくさんあります。

    • サービスワーカーの考え方。これは、サービスワーカーが舞台裏でどのように機能するか、およびサービスワーカーを構築するときに理解することを理解するのに役立ちます。
    • Chris Ferdinandiは、サービスワーカーに関する一連のすばらしい記事を提供し、オフラインアプリケーションの作成方法を説明し、最近表示したページをオフラインで保存する方法から、サービスワーカーのキャッシュにアイテムの有効期限を設定する方法まで、さまざまなシナリオをカバーしています。

    • サービスワーカーの落とし穴とベストプラクティス、スコープに関するいくつかのヒント、サービスワーカーの登録とサービスワーカーのキャッシュの遅延。
    • Ire Aderinokunによる、Service Workerによる「オフラインファースト」の素晴らしいシリーズで、アプリシェルのプリキャッシングに関する戦略があります。
    • Service Worker:豊富なオフラインエクスペリエンス、定期的なバックグラウンド同期、プッシュ通知のためにServiceWorkerを使用する方法に関する実用的なヒントの紹介。
    • 古き良きジェイク・アーチボルドのオフラインクックブックを参照して、自分のサービスワーカーを焼く方法に関するレシピをいくつか紹介することは常に価値があります。
    • Workboxは、プログレッシブWebアプリを構築するために特別に構築されたServiceWorkerライブラリのセットです。
  2. たとえばA / Bテストのために、CDN / Edgeでサーバーワーカーを実行していますか?
    この時点では、クライアントでサービスワーカーを実行することにかなり慣れていますが、CDNをサーバーに実装すると、エッジでのパフォーマンスを微調整するためにも使用できます。

    たとえば、A / Bテストでは、HTMLがユーザーごとにコンテンツを変更する必要がある場合、CDNサーバー上のServiceWorkerを使用してロジックを処理できます。 HTMLの書き換えをストリーミングして、GoogleFontsを使用するサイトを高速化することもできます。

2016年1月から2020年7月までの時間の経過に伴うページの割合を含む、デスクトップとモバイルでのServiceWorkerのインストールの時系列を示すグラフ
ServiceWorkerのインストールの時系列。 Web Almanacによると、すべてのデスクトップページの0.87%だけがサービスワーカーを登録しています。 (大プレビュー)
  1. レンダリングパフォーマンスを最適化します。
    アプリケーションが遅いときはいつでも、すぐに目立ちます。 したがって、ページをスクロールするとき、または要素がアニメーション化されるときにラグがないこと、および1秒あたり60フレームを常にヒットしていることを確認する必要があります。 それが不可能な場合は、少なくとも1秒あたりのフレーム数を一定にすることが60〜15の混合範囲よりも望ましいです。CSSのwill-changeを使用して、どの要素とプロパティが変更されるかをブラウザに通知します。

    発生しているときはいつでも、DevToolsで不要な再描画をデバッグしてください。

    • ランタイムレンダリングのパフォーマンスを測定します。 それを理解する方法に関するいくつかの役立つヒントを確認してください。
    • 開始するには、ブラウザレンダリングの最適化に関するPaul Lewisの無料のUdacityコースと、ブラウザのペイントとWebパフォーマンスに関する考慮事項に関するGeorgyMarchukの記事を確認してください。
    • FirefoxDevToolsの「その他のツール→レンダリング→ペイントフラッシュ」でペイントフラッシュを有効にします。
    • React DevToolsで、[更新を強調表示]をオンにし、[各コンポーネントがレンダリングされた理由を記録する]を有効にします。
    • また、Why Did You Renderを使用することもできるため、コンポーネントが再レンダリングされると、フラッシュが変更を通知します。

    組積造のレイアウトを使用していますか? 間もなく、CSSグリッドだけで石積みレイアウトを構築できる可能性があることに注意してください。

    このトピックをさらに深く掘り下げたい場合は、Nolan Lawsonが彼の記事でレイアウトのパフォーマンスを正確に測定するための秘訣を共有しており、JasonMillerも代替手法を提案しています。 また、GPUアニメーションを正しくする方法についてのSergeyChikuyonokによるlilの記事があります。

    位置、スケール、回転、不透明度などの高性能アニメーション
    ブラウザは、変換と不透明度を安価にアニメーション化できます。 CSSトリガーは、CSSがリレイアウトまたはリフローをトリガーするかどうかを確認するのに役立ちます。 (画像クレジット:Addy Osmani)(大プレビュー)

    :GPU合成レイヤーへの変更は最も安価であるため、 opacitytransformを介して合成のみをトリガーすることで回避できる場合は、正しい方向に進んでいます。 Anna Migasは、UIレンダリングパフォーマンスのデバッグに関する講演でも多くの実践的なアドバイスを提供しています。 また、DevToolsでペイントパフォーマンスをデバッグする方法を理解するには、Umarのペイントパフォーマンス監査ビデオを確認してください。

  2. 知覚されるパフォーマンスに合わせて最適化しましたか?
    コンポーネントがページにどのように表示されるか、およびアセットをブラウザーに提供する方法の戦略は重要ですが、知覚されるパフォーマンスの役割も過小評価してはなりません。 このコンセプトは、待機の心理的側面を扱い、基本的に、何か他のことが起こっている間、顧客を忙しくしたり、従事させたりします。 そこで、知覚管理、先制的開始、早期完了、および許容度管理が機能します。

    それはどういう意味ですか? アセットをロードしている間、私たちは常にお客様の一歩先を行くように努めることができるので、バックグラウンドでかなり多くのことが起こっている間、経験は迅速に感じられます。 顧客の関心を維持するために、インジケーターをロードする代わりにスケルトン画面をテストし(実装デモ)、トランジション/アニメーションを追加し、基本的に最適化するものがないときにUXをごまかすことができます。

    The Art of UI Skeletonsのケーススタディで、Kumar McMillanは、動的リスト、テキスト、最終画面をシミュレートする方法、およびReactでスケルトン思考を検討する方法に関するいくつかのアイデアとテクニックを共有しています。

    ただし、注意してください。一部のテストでは、スケルトンスクリーンのパフォーマンスがすべてのメトリックで最悪であることが示されたため、スケルトンスクリーンはデプロイする前にテストする必要があります。

  3. レイアウトのずれや塗り直しを防ぎますか?
    知覚されるパフォーマンスの領域では、おそらくより破壊的なエクスペリエンスの1つは、再スケーリングされた画像やビデオ、Webフォント、挿入された広告、またはコンポーネントに実際のコンテンツを取り込む最近発見されたスクリプトによって引き起こされるレイアウトシフトまたはリフローです。 その結果、顧客は、読書エリアの上のレイアウトジャンプによって中断されるだけで記事を読み始める可能性があります。 経験はしばしば突然で非常に混乱します:そしてそれはおそらく再考される必要がある優先順位をロードする場合です。

    コミュニティは、リフローを回避するためのいくつかの手法と回避策を開発しました。 一般に、ユーザーの操作に応じて発生する場合を除いて、既存のコンテンツの上に新しいコンテンツを挿入しないことをお勧めします。 画像には常に幅と高さの属性を設定するため、最近のブラウザはボックスを割り当て、デフォルトでスペースを予約します(Firefox、Chrome)。

    画像と動画の両方で、SVGプレースホルダーを使用して、メディアが表示される表示ボックスを予約できます。つまり、アスペクト比を維持する必要がある場合にも、領域が適切に予約されます。 広告や動的コンテンツにプレースホルダーやフォールバック画像を使用したり、レイアウトスロットを事前に割り当てたりすることもできます。

    外部スクリプトを使用して画像を遅延読み込みする代わりに、ネイティブ遅延読み込みを使用するか、ネイティブ遅延読み込みがサポートされていない場合にのみ外部スクリプトを読み込むときにハイブリッド遅延読み込みを使用することを検討してください。

    上記のように、常にWebフォントの再描画をグループ化し、すべてのフォールバックフォントからすべてのWebフォントに一度に移行します。font-style-matcherを使用してフォント間の行の高さと間隔を調整することにより、切り替えが急激になりすぎないようにします。 。

    フォールバックフォントのフォントメトリックをオーバーライドしてWebフォントをエミュレートするには、@ font-face記述子を使用してフォントメトリックをオーバーライドできます(デモ、Chrome 87で有効)。 (ただし、複雑なフォントスタックでは調整が複雑になることに注意してください。)

    最近のCSSの場合、レイアウトが重要なCSSが各テンプレートのヘッダーにインライン化されていることを確認できます。 さらに、長いページの場合、垂直スクロールバーを追加すると、メインコンテンツが16px左にシフトします。 スクロールバーを早期に表示するために、 overflow-y: scrollを追加できますhtmlをスクロールして、最初のペイントでスクロールバーを適用します。 後者は、幅が変更されたときにスクロールバーが折り目の上のコンテンツのリフローのために重要なレイアウトシフトを引き起こす可能性があるため、役立ちます。 ただし、ほとんどの場合、Windowsのような非オーバーレイスクロールバーを備えたプラットフォームで発生するはずです。 しかし: position: stickyです。

    スクロール時にページの上部に固定または固定されたヘッダーを処理する場合は、コンテンツのプレースホルダー要素やmargin-topなど、ヘッダーがパインになったときにヘッダー用のスペースを予約します。 例外は、CLSに影響を与えてはならないCookie同意バナーですが、影響を与える場合もあります。実装によって異なります。 このTwitterスレッドには、いくつかの興味深い戦略とポイントがあります。

    さまざまな量のテキストが含まれる可能性のあるタブコンポーネントの場合、CSSグリッドスタックを使用してレイアウトのシフトを防ぐことができます。 各タブのコンテンツを同じグリッド領域に配置し、一度に1つを非表示にすることで、コンテナが常に大きい要素の高さを占めるようにすることができるため、レイアウトのずれは発生しません。

    ああ、そしてもちろん、リストの下にコンテンツ(フッターなど)がある場合は、無限のスクロールと「さらに読み込む」によってレイアウトがシフトする可能性があります。 CLSを改善するには、ユーザーがページのその部分にスクロールするに読み込まれるコンテンツ用に十分なスペースを確保し、コンテンツの読み込みによって押し下げられる可能性のあるページ下部のフッターまたはDOM要素を削除します。また、折り畳み下のコンテンツのデータと画像をプリフェッチして、ユーザーがそこまでスクロールするまでに、すでにそこにあるようにします。 react-windowのようなリスト仮想化ライブラリを使用して長いリストを最適化することもできます(ありがとう、Addy Osmani! )。

    リフローの影響を確実に抑えるには、Layout InstabilityAPIを使用してレイアウトの安定性を測定します。 これを使用すると、累積レイアウトシフト( CLS )スコアを計算し、それをテストの要件として含めることができるため、リグレッションが発生した場合はいつでも、それを追跡して修正できます。

    レイアウトシフトスコアを計算するために、ブラウザはビューポートのサイズと、レンダリングされた2つのフレーム間のビューポート内の不安定な要素の動きを調べます。 理想的には、スコアは0に近くなります。 CLSとは何か、CLSの測定方法については、MilicaMihajlijaとPhilipWaltonによるすばらしいガイドがあります。 これは、特にビジネスクリティカルなタスクの場合に、知覚されるパフォーマンスを測定および維持し、中断を回避するための良い出発点です。

    クイックヒント:DevToolsでレイアウトシフトの原因を見つけるには、パフォーマンスパネルの[エクスペリエンス]でレイアウトシフトを調べることができます。

    ボーナス:リフローとリペイントを減らしたい場合は、CharisTheodoulouのDOMリフロー/レイアウトスラッシングの最小化ガイドとPaulIrishのレイアウト/リフローを強制するもののリストとCSSTriggers.com、レイアウトをトリガーするCSSプロパティのリファレンステーブル、ペイントを確認してくださいと合成。

目次

  1. 準備:計画と指標
  2. 現実的な目標の設定
  3. 環境の定義
  4. 資産の最適化
  5. ビルドの最適化
  6. 配信の最適化
  7. ネットワーキング、HTTP / 2、HTTP / 3
  8. テストとモニタリング
  9. クイックウィン
  10. 1ページのすべて
  11. チェックリストをダウンロードする(PDF、Apple Pages、MS Word)
  12. 次のガイドを見逃さないように、メールマガジンを購読してください。