クリティカルレンダリングパス
クリティカルレンダリングパスとは、ブラウザーが HTML、CSS、JavaScript を画面上のピクセルに変換する際に経る一連のステップです。クリティカルレンダリングパスを最適化すると、レンダリングパフォーマンスが改善されます。 クリティカルレンダリングパスには、ドキュメントオブジェクトモデル (DOM)、CSS オブジェクトモデル (CSSOM)、レンダリングツリー、レイアウトを含みます。
ドキュメントオブジェクトモデルはHTMLの解析中に生成されます。HTML は JavaScript をリクエストすることがあり、JavaScript は DOM を変更することがあります。HTML はスタイルを含んでいたりリクエストしたりし、それによって CSS オブジェクトモデルが構築されます。ブラウザーエンジンはこれら二つを組み合わせてレンダリングツリーを作成します。レイアウトはページ上のすべての要素のサイズと位置を決定します。レイアウトが決定されると、ピクセルが画面に描画されます。
クリティカルレンダリングパスを最適化することで、最初のレンダリングまでの時間を改善することができます。クリティカルレンダリングパスを理解し最適化することは、再レイアウトや再描画が 60 フレーム毎秒で現れるように実現し、パフォーマンスの高いユーザー操作を確保し、ジャンクを避けることができます。
CRP を理解する
ウェブのパフォーマンスには、サーバーへのリクエストとレスポンス、読み込み、スクリプティング、レンダリング、レイアウト、そして画面へのピクセル描画が含まれます。
ウェブページやアプリのリクエストは HTTP リクエストから始まります。サーバーは HTML を含むレスポンスを送信します。ブラウザーは HTML の構文解析を開始し、受信したバイト列を DOM ツリーに変換します。ブラウザーは、スタイルシート、スクリプト、埋め込み画像への参照など、外部リソースへのリンクを見つけるたびにリクエストを開始します。一部のリクエストはブロッキングであり、インポートされたアセットが処理されるまで HTML の残りの部分の構文解析が停止されます。ブラウザーはリクエストを行いながら、 DOM を構築しつつ HTML の構文解析を続けます。 HTML の終わりに到達すると、 CSS オブジェクトモデル (CSSOM) を構築します。 DOM と CSSOM が完成すると、ブラウザーはレンダリングツリーを構築し、表示されるすべてのコンテンツのスタイルを計算します。レンダリングツリーの構築が完了した後、レイアウトが発生し、レンダリングツリー内のすべての要素の位置とサイズが定義されます。これが完了すると、ページがレンダリングされ、画面上に「描画」されます。
ドキュメントオブジェクトモデル
DOM の構築は段階的に行われます。 HTML レスポンスはトークンに変換され、トークンはノードに変換され、ノードは DOM ツリーに変換されます。単一の DOM ノードは開始タグトークンで始まり、終了タグトークンで終わります。ノードは HTML 要素に関するすべての関連情報を含みます。この情報はトークンを用いて記述されます。ノードはトークンの階層構造に基づいて DOM ツリーに接続されます。開始タグと終了タグのセットの間に別の開始タグと終了タグのセットが存在する場合、それはノード内のノードとなり、これが DOM ツリーの階層構造を定義する方法です。
ノード数が増えるほど、クリティカルレンダリングパスにおける次のイベントの実行時間が長くなります。測定を!いくつか追加のノードでは大きな差は生じませんが、多数のノードを追加するとパフォーマンスに影響を与えることを念頭に置いてください。
CSS オブジェクトモデル
DOM にはページのすべてのコンテンツが含まれます。 CSSOM には DOM のスタイル設定に関するすべての情報が含まれます。 CSSOM は DOM と似ていますが、異なります。 DOM の構築は段階的ですが、 CSSOM はそうではありません。 CSS はレンダリングをブロックします。ブラウザーは全ての CSS を受信・処理するまでページのレンダリングをブロックします。 CSS がレンダリングをブロックするのは、ルールが上書きされる可能性があるためです。そのため、 CSSOM が完成させるまでコンテンツはレンダリングされません。
CSS には有効なトークンを識別するための独自の一連のルールがあります。 CSS の C は「カスケード」を意味することを思い出してください。 CSS ルールはカスケードします。パーサーがトークンをノードに変換するにつれ、子孫ノードは親ノードのスタイルの一部を継承します。 HTML 同様に、増分処理機能はCSSに適用されないのは、後続のルールが前回ルールを上書きする可能性があるためです。 CSS オブジェクトモデルは CSS の構文解析中に取得され、しかし完全に解析が完了するまでレンダリングツリーの構築には使用されません。後続の解析で上書きされるスタイルは画面に描画するべきではないためです。
セレクターのパフォーマンスに関しては、詳細でないセレクターほど高速です。例えば、.foo {}
は .bar .foo {}
よりも高速です。なぜなら、ブラウザーが .foo
を検出する際、後者のシナリオでは .foo
が祖先要素 .bar
を持つかどうかを確認するために DOM を遡って検査する必要があるからです。より詳細なタグはブラウザーに追加の処理を要求しますが、このオーバーヘッドは最適化の対象とする価値があるとは考えにくいでしょう。
CSS の構文解析にかかる時間を計測すると、ブラウザーの実行速度の速さに驚くでしょう。より詳細なルールほどコストが高くなります。 DOM ツリー内のより多くのノードを走査する必要があるためです。しかし、その追加コストは概してごくわずかです。最初の計測を行い、必要に応じて最適化してください。特異性は、おそらく最も手っ取り早く改善できる部分ではありません。 CSS に関しては、セレクターのパフォーマンス最適化による改善は、せいぜいマイクロ秒単位です。それ以外にも、 CSS を最適化する方法は、ミニファイや、メディアクエリーを使用して遅延読み込み CSS を非ブロックリクエストに分離するといった手法もあります。
レンダリングツリー
レンダリングツリーはコンテンツとスタイルの両方を捕捉します。 DOM ツリーと CSSOM ツリーが結合されて、レンダリングツリーが形成されます。レンダリングツリーを構築するため、ブラウザーは DOM ツリーのルートから始まる各ノードを調べて、どの CSS ルールが添付されるかを決定します。
レンダリングツリーは可視コンテンツのみを捕捉します。 head セクションは(一般的に)可視情報を含まないため、レンダリングツリーに含まれません。要素に display: none;
が設定されている場合、その要素も子孫要素もレンダリングツリーに含まれません。
レイアウト
レンダリングツリーが構築されると、レイアウトが可能になります。レイアウトは画面サイズに依存します。レイアウト段階では、要素がページ上にどこにどのように位置指定されるかが決定され、それぞれの要素の幅と高さが定められ、要素同士の位置関係が決められます。
要素の幅とは何でしょうか?定義上、ブロックレベル要素は親要素の幅の 100% を既定の幅とします。幅 50% の要素は親要素の幅の半分になります。特に指定がない限り、 body 要素の幅は 100% であり、ビューポートの幅の 100% ということです。これは端末の幅がレイアウトに影響を与えることです。
ビューポートメタタグは、レイアウトビューポートの幅を定義し、レイアウトに影響を与えます。これを設定しない場合、ブラウザーは既定のビューポート幅を使用します。既定では全画面表示されるブラウザーでは、通常 960px です。既定で全画面表示されるブラウザー(スマートフォンのブラウザーなど)では、 <meta name="viewport" content="width=device-width">
を設定することで、既定のビューポート幅ではなく端末の幅が適用されます。この端末幅は、ユーザーがスマートフォンを横向きと縦向きで回転させる時点ごとに変化します。レイアウトは、端末が回転されるか、ブラウザーのサイズが変更される時点ごとに再実行されます。
レイアウトのパフォーマンスは DOM の影響を受けます。ノード数が多いほど、詳しいレイアウトに時間がかかります。レイアウトはボトルネックとなり、スクロールやその他のアニメーション中に必要とされる場合、ジャンクを引き起こす可能性があります。ロード時や方向変更時の 20ms の遅延は許容範囲でも、アニメーションやスクロール時にはジャンクが発生します。ノードを追加する、コンテンツを変更する、またはノードのボックスモデルスタイルを設定するなどの理由でレンダリングツリーが変更されるたびにレイアウトが発生します。
レイアウトイベントの頻度と再生時間を縮小するためには、一括更新を行い、ボックスモデルのプロパティのアニメーションを避けることができます。
描画
最後の段階は画面へのピクセル描画です。レンダリングツリーが作成されレイアウトが完了すると、ピクセルを画面に描画できます。読み込む際には画面全体が描画されます。その後は、ブラウザーが最小の領域のみを再描画するよう最適化されているため、画面の該当領域のみが再描画されます。描画時点は、レンダリングツリーに適用される更新の種類によって異なります。描画自体はとても高速な処理であるため、パフォーマンス改善する場所として最も影響力のある場所とはならない可能性が高いですが、アニメーションフレームの所要時間を測定する際には、レイアウト時間と再描画時間の両方を考慮に入れることが重要です。それぞれのノードに適用されるスタイルは描画時間を増加させますが、描画時間を 0.001ms 増加させるスタイルを除去しても、最適化の費用対効果が最大とは限りません。最初の測定を行うことを忘れないでください。その後、それが最適化の優先事項であるべきかどうかを判断できます。
CRP のための最適化
ページ読み込み速度を改善するには、リソースが読み込まれる優先度を設定し、読み込み順序を制御し、リソースのファイルサイズを縮小します。パフォーマンス向上のためのヒントは以下の通りです。 1) 非必須リソースのダウンロードを遅延させる、非同期としてマークする、あるいは完全に削除することで、必須リソースの数を最小限に抑える。 2) 必要なリクエスト数とそれぞれのリクエストのファイルサイズを最適化する。 3) 必須アセットのダウンロードを優先することで必須リソースの読み込み順序を最適化し、クリティカルパスの長さを短縮する。