ブラウザの「再計算」という名の重労働:スタイル再計算(Recalculate Style)の舞台裏
現場で「なぜかUIの反応がもっさりしている」と感じたことはないだろうか。JavaScriptでDOMをいじり、クラスを付け替える。何気ない操作に見えるが、ブラウザの内部では壮絶な「計算の嵐」が吹き荒れている。
今日は、その中心部にある「スタイル再計算(Recalculate Style)」という処理について、現場の勘所を交えながら深掘りしていこう。ここを制する者が、パフォーマンスの壁を突破できると言っても過言じゃない。
—
ブラウザは「全知全能」ではない:スタイル再計算の仕組み
DOMツリーが構築された後、ブラウザはCSSOM(CSS Object Model)を構築し、それらを組み合わせて「レンダーツリー」を作る。問題は、DOMが動的に変更された瞬間だ。
ブラウザは「どの要素に、どのスタイルが適用されるべきか」を再判定しなければならない。これが「Recalculate Style」の正体だ。
1. スタイルのマッチング: DOMの変更点から影響を受ける要素を特定し、適用可能なCSSルールを検索する。
2. カスケードの解決: CSSの優先順位(特異度や継承、インラインスタイルなど)を計算して、最終的な値を算出する。
この際、ブラウザは単にそのノードだけを見るわけではない。親要素から継承されるプロパティがあれば、DOMツリーを再帰的に辿る必要がある。 つまり、ページ全体の構造が深ければ深いほど、この計算コストは指数関数的に跳ね上がるんだ。
パフォーマンスを食いつぶす「レイアウト・スラッシング」
実務で一番やってはいけないのが、「読み取り」と「書き込み」の交互実行だ。
例えば、`element.offsetHeight` を参照すると、ブラウザは「最新のスタイルを確定させなきゃいけない」と判断し、強制的にスタイル再計算とレイアウト(Reflow)を走らせる。これをループの中で繰り返すと、ブラウザは計算地獄に陥る。これが「レイアウト・スラッシング」だ。
—
実践:パフォーマンスを最適化する「書き込み」の流儀
では、どうすればこの無駄な再計算を減らせるか。鉄則は「読み込みを先に、書き込みを後に」まとめることだ。
以下のコードを見てほしい。悪い例と、現場で好まれる「バッチ処理」的なアプローチの違いだ。
/
- 【改善前:アンチパターン】
- ループ内で読み書きを繰り返すと、ブラウザは毎回スタイル再計算を強制される
/
function updateLayoutBad(elements) {
elements.forEach(el => {
const height = el.offsetHeight; // 読み込み(強制同期レイアウト発生!)
el.style.height = (height + 10) + ‘px’; // 書き込み
});
}
/
- 【改善後:ベストプラクティス】
- 読み込みを先に終わらせ、その後で一気に書き込むことで再計算の回数を最小化する
/
function updateLayoutGood(elements) {
// 1. まず読み込み(DOMの読み取りだけを先に済ませる)
const heights = elements.map(el => el.offsetHeight);
// 2. 次に書き込み(ここでまとめてスタイルを適用する)
elements.forEach((el, index) => {
el.style.height = (heights[index] + 10) + ‘px’;
});
// ブラウザはこの後、適切なタイミング(次のフレーム)で一度だけ計算を行う
}
チーフアーキテクトからの助言
- CSSクラスの一括更新: スタイルを一つずつ `el.style.color = ‘red’` のように当てるのではなく、あらかじめ定義したクラスを `el.classList.add(‘is-active’)` で切り替える方が圧倒的に効率的だ。ブラウザの最適化エンジンは、クラス単位の変更を極めて高速に処理できる。
- `contain` プロパティの活用: CSSの `contain: layout;` や `contain: paint;` を使うと、その要素以下での変更が親要素に影響しないことをブラウザに教えることができる。これにより、再計算の範囲を局所化できる。これは大規模なSPAを作る際には必須の知識だ。
- DevToolsを相棒にせよ: Chrome DevToolsの「Performance」タブを見れば、Recalculate Styleにどれだけの時間がかかっているか一目瞭然だ。紫色やオレンジ色のバーが長く伸びている箇所があれば、そこが君の改善ポイントだ。
—
最後に:ブラウザと対話する
ブラウザは君のコードを忠実に実行しようと必死に働いている。その努力を台無しにするのも、助けてあげるのも、すべてはエンジニアである君の書き方次第だ。
「なぜ動くか」だけでなく、「なぜブラウザは今、これほど計算に時間をかけているのか」を想像できるようになると、フロントエンドの景色はガラリと変わるはずだ。
さあ、エディタを開いて、君のコードの「計算コスト」を削ぎ落としてみよう。現場からは以上だ。

コメント