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

JavaScript は、多くの場合、視覚的な変化のトリガーとなります。スタイル操作によって直接変更する場合もあれば、データの検索や並べ替えなど、視覚的な変化をもたらす計算によって変更する場合もあります。タイミングの悪い JavaScript や実行時間の長い JavaScript は、パフォーマンスの問題の一般的な原因となる可能性があるため、可能な限り影響を最小限に抑える必要があります。

スタイルの計算

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

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

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

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

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

セレクタの複雑さを軽減する

CSS セレクタを簡略化すると、ページのスタイル計算を高速化できます。 最もシンプルなセレクタは、クラス名だけで CSS の要素を参照します。

.title {
  /* styles */
}

ただし、プロジェクトが拡大するにつれて、より複雑な CSS が必要になる可能性があり、次のようなセレクタになる可能性があります。

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

これらのスタイルがページにどのように適用されるかを判断するために、ブラウザは「これは、クラス title の要素で、親がクラス box で、親要素のマイナス nth プラス 1 の子ですか?」と効果的に尋ねる必要があります。 これを把握するには、ブラウザに時間がかかることがあります。これを簡略化するには、セレクタをより具体的なクラス名に変更します。

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

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

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

パフォーマンスに関するもう 1 つの考慮事項(セレクタの複雑さよりも重要な場合が多い)は、要素が変更されたときに必要な作業量です。

一般に、計算された要素のスタイルの計算の最悪の場合のコストは、要素の数にセレクタ数を掛けた値になります。これは、ブラウザが一致するかどうかを確認するために、すべてのスタイルに対して各要素を少なくとも 1 回チェックする必要があるためです。

スタイルの計算では、ページ全体を無効にするのではなく、少数の要素を直接ターゲットにできます。最新のブラウザでは、変更が影響する可能性のあるすべての要素を常にチェックする必要がないため、これは問題になりにくい傾向があります。 一方、古いブラウザは、このようなタスクに最適化されていない場合があります。可能な場合は、無効な要素の数を減らす 必要があります。

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

ブラウザでのスタイルの再計算のコストを測定する方法はいくつかあります。どちらの方法も、開発環境のブラウザで測定するか、ウェブサイトの実際のユーザーに対してこのプロセスにかかる時間を測定するかによって異なります。

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

スタイルの再計算のコストを測定する方法の 1 つに、Chrome DevTools の [Performance] パネルを使用する方法があります。まず、次の操作を行います。

  1. DevTools を開きます。
  2. [Performance] タブに移動します。
  3. [**Selector stats**] チェックボックスをオンにします(省略可)。
  4. [Record] をクリックします。
  5. ページを操作します。

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

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

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

Chrome DevTools のパフォーマンス パネルにデータが入力されたアクティビティの概要で、Chrome DevTools の問題領域を拡大表示している。
DevTools アクティビティの実行時間の長いフレーム 概要。

スクロールなどのインタラクション中に実行時間の長いフレームが表示される場合は、詳しく調べてください。大きな紫色のブロックが表示されたら、アクティビティを拡大し、[Recalculate Style] というラベルの付いた作業を選択して、コストの高いスタイルの再計算作業の詳細を確認します。

スタイル再計算の処理によって影響を受ける要素の数などの重要な情報を含む、長時間実行されるスタイル計算の詳細を取得します。
DevTools の概要で 25 ミリ秒強かかる実行時間の長いスタイルの再計算。

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

トレースを実行する前に [Performance Panel] 設定で [Selector stats] チェックボックスをオンにすると、トレースの下部 パネルに同じ名前のタブが追加されます。

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

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

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

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

ウェブサイトの 実際のユーザーに対してスタイルの再計算が発生するまでにどのくらいの時間がかかるかを知りたい場合は、Long Animation Frames API を使用すると、必要なツールを利用できます。この API のデータは、the web-vitals JavaScript ライブラリに、 スタイルの再計算時間を含め、追加されました。

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

リソース