スタイル計算の範囲と複雑さを軽減

JavaScript は一般に、視覚変化のトリガーとなるものです。視覚変化はスタイル操作を通じて直接行われることもあれば、データの検索やソートのように、計算が最終的に視覚変化につながることもあります。タイミングの悪い JavaScript や長時間実行される JavaScript はパフォーマンス低下の原因になることが多いため、可能な限り JavaScript の影響を最小限に抑える必要があります。

スタイルの計算

要素の追加や削除、属性やクラスの変更、アニメーションの再生によって DOM を変更すると、ブラウザで要素スタイルの再計算が発生するほか、多くの場合、ページ全体またはページの一部のレイアウトが再計算されます。このプロセスは「スタイル計算」と呼ばれます。

ブラウザは、適合セレクターのセットを作成して、特定の要素に適用されるクラス、疑似セレクター、ID を決定することで、スタイルの計算を開始します。次に、一致するセレクタのスタイルルールを処理し、要素の最終的なスタイルを確定します。

インタラクション レイテンシにおけるスタイル再計算の役割

Interaction to Next Paint(INP)は、ユーザー入力に対するページの全体的な応答性を評価する、ユーザー中心のランタイム パフォーマンス指標です。ユーザーがページを操作してから、ブラウザがユーザー インターフェースの対応する視覚的な更新を示す次のフレームをペイントするまでのインタラクション レイテンシを測定します。

インタラクションの重要な要素は、次のフレームのペイントにかかる時間です。次のフレームを表示するために行われるレンダリング処理は、レイアウト、ペイント、合成処理の直前に行われるページスタイルの計算など、多くの部分で構成されています。このガイドではスタイル計算のコストに焦点を当てていますが、インタラクションの合計レンダリング時間の一部を短縮すると、合計レイテンシも短縮されます。

セレクターの複雑性の軽減

CSS セレクタを簡素化すると、ページのスタイル計算を高速化できます。最も単純なセレクタは、クラス名のみを使用して CSS 内の要素を参照します。

.title {
  /* styles */
}

しかしながら、プロジェクトの拡張に伴って、CSS はより複雑になり、最終的には次のようなセレクターを使用することになります。

.box:nth-last-child(-n+1) .title {
  /* styles */
}

これらのスタイルがページにどのように適用されるかを判断するために、ブラウザは「これは title クラスの要素で、親要素の n 番目の子である親要素の box クラスの親を持つ要素なのか」を判断しなければなりません。ブラウザがこれを判断するまでに時間がかかる場合もあります。これを簡素化するために、セレクタをより具体的なクラス名に変更できます。

.final-box-title {
  /* styles */
}

これらの置換クラス名は不自然に見えるかもしれませんが、ブラウザの処理が大幅に簡素化されます。たとえば、以前のバージョンでは、ブラウザが要素がそのタイプの最後であることを認識するには、まず他のすべての要素についてすべてを把握し、その後に続く要素が nth-last-child であるかどうかを判断する必要がありました。これは、クラス名のみに基づいてセレクタを要素に照合するよりも、計算コストが大幅に増加する可能性があります。

スタイル設定する要素の数を減らす

パフォーマンスに関するもう 1 つの考慮事項(多くの場合、セレクタの複雑さよりも重要)は、要素の変更時に実行する必要のある作業の量です。

スタイル計算では、各要素を少なくとも 1 回は全スタイルとチェックして、スタイルが一致するかどうかを確認する必要があります。そのため、一般的に、要素のスタイル計算を実行するときの最悪ケースのコストは、要素の数にセレクターの数を掛けた積に比例します。

スタイル計算では、ページ全体を無効にする代わりに、少数の要素を直接対象にできます。ブラウザは必ずしも変更によって影響を受ける可能性のあるすべての要素をチェックする必要はないため、最近のブラウザでは、これは大きな問題にはなりません。一方、古いブラウザはこのような処理に対して最適化されていない場合があります。可能な場合は、無効化される要素の数を減らす必要があります。

スタイル再計算コストを測定する

ブラウザでスタイルの再計算のコストを確認する方法はいくつかあります。それぞれ、開発環境のブラウザで測定するのか、ウェブサイトで実際のユーザーがこの処理に要する時間を測定するのかによって異なります。

Chrome DevTools でスタイル再計算コストを測定する

スタイル再計算のコストを測定する方法の一つは、Chrome DevTools のパフォーマンス パネルを使用することです。利用を開始する手順は次のとおりです。

  1. DevTools を開きます。
  2. [パフォーマンス] タブに移動します。
  3. [セレクタの統計情報] チェックボックスをオンにします(省略可)。
  4. [録音] をクリックします。
  5. ページを操作します。

録画を停止すると、次のような画像が表示されます。

スタイル計算を示す DevTools。
スタイル計算を示す DevTools レポート。

上部のストリップは、フレームレートもプロットするミニチュア フレームグラフです。アクティビティがストリップの下部に近いほど、ブラウザによるフレームのペイントが速くなります。炎のグラフが上部で平坦になり、その上に赤いバーが表示されている場合は、長時間実行されているフレームの原因となる処理があります。

Chrome DevTools のパフォーマンス パネルに表示されたアクティビティの概要で、Chrome DevTools のトラブル領域を拡大表示しています。
DevTools のアクティビティの概要で長時間実行されるフレーム。

スクロールなどのインタラクション中に長時間実行されているフレームは、詳しく調べる価値があります。大きな紫色のブロックが表示された場合は、アクティビティをズームインし、[スタイルの再計算] というラベルの付いた作業を選択して、費用のかかる可能性があるスタイルの再計算作業の詳細を確認します。

長時間実行されているスタイル計算の詳細を取得します。これには、スタイルの再計算作業の影響を受ける要素の数などの重要な情報が含まれます。
DevTools の概要で、25 ミリ秒を超える長時間のスタイル再計算。

イベントをクリックすると、コールスタックが表示されます。レンダリング処理がユーザー操作によって発生した場合は、スタイル変更をトリガーした JavaScript を呼び出します。また、変更が影響する要素の数(この例では 900 要素以上)と、スタイル計算にかかった時間も表示されます。この情報を使用して、コード修正の試行を開始できます。

トレースを行う前に [パフォーマンス パネル] の設定で [セレクタの統計情報] チェックボックスをオンにしていた場合、トレース下部のパネルに同じ名前のタブが追加されます。

Chrome DevTools のパフォーマンス パネルに表示される CSS セレクタの統計情報の表。この表には、経過時間、一致試行回数、一致数、一致しないノードの割合、セレクタ、セレクタが見つかるスタイルシートなどのヘッダーと対応するデータが含まれます。
Chrome DevTools のパフォーマンス パネルに表示されるセレクタの統計情報の表。

このパネルには、各セレクタの相対的なコストに関する有用なデータが表示されるため、コストの高い CSS セレクタを特定できます。

詳細については、CSS セレクタの統計情報のドキュメントをご覧ください。

実際のユーザーのスタイルの再計算コストを測定する

ウェブサイトの実際のユーザーに対してスタイルの再計算が行われるまでにかかる時間を把握したい場合は、Long Animation Frames API を使用すると必要なツールが提供されます。この API のデータは、スタイルの再計算時間など、web-vitals JavaScript ライブラリに追加されています。

インタラクションの表示遅延がページの INP の主な原因と思われる場合は、その時間のうち、ページのスタイルの再計算に費やされている時間の割合を把握する必要があります。詳細については、フィールドでスタイルの再計算時間を測定する方法をご覧ください。

リソース