やあ。現場でコードを書いていて、「なぜかカクつく」「レンダリングが重い」という壁にぶつかったことはないかい?
ブラウザのレンダリングパイプラインを理解することは、フロントエンドエンジニアにとっての「解剖学」だ。ここを避けて通る者は、いつまで経っても「なぜか直らないCSS」や「謎の重さ」に振り回されることになる。
今日は、ブラウザが裏側で何をやっているのか、その泥臭い現場を覗いてみよう。
—
レンダリングパイプラインの深層:ブラウザは「何」をしているのか
ブラウザがHTMLを受け取ってから画面にピクセルを描画するまでには、大きく分けて以下の工程がある。
1. DOM/CSSOM構築: HTML/CSSの解析。まずは素材集め。
2. Recalculate Style: DOMとCSSOMをマージして、「どのノードにどのスタイルが当たるか」を計算する。
3. Layout (Reflow): 各ノードの幾何学的な位置とサイズを計算する。これが一番の重労働だ。
4. Paint: 色、影、境界線などをビットマップに描画する。
5. Composite: 描画されたレイヤーを重ね合わせ、GPUに送って画面に出力する。
多くのエンジニアが陥る罠は、「JavaScriptでDOMをいじると、このパイプラインがどこから再走するのか」を意識していないことにある。
1. Recalculate Style(スタイルの再計算)
JSでクラスを書き換えると、ブラウザは「影響を受けるノードはどこだ?」と木構造を辿り始める。CSSセレクタが複雑であればあるほど、この探索コストは跳ね上がる。
2. Layout(レイアウト)
要素の幅や高さを変えると、その子要素や親要素、隣接する要素の位置まで再計算が必要になる。これが「リフロー」だ。特に`flex`や`grid`を多用していると、計算量は指数関数的に増える可能性がある。
3. Paint(ペイント)
背景色や文字色を変えるだけならレイアウトは走らないが、ペイントは走る。これ自体はさほど重くないが、範囲が広ければ相応のコストがかかる。
4. Composite(コンポジット)
ここが現代のレンダリングの要だ。「CSSの `transform` や `opacity` だけを変える」と、レイアウトもペイントもスキップして、GPUで画像を動かすだけの「コンポジット」だけで完結する。これが60fps(あるいは120fps)を維持する魔法の領域だ。
—
ボトルネックを特定する:Chrome DevToolsの「真実」
勘でコードを書くのはやめよう。Chrome DevToolsの 「Performance」タブ を開くんだ。
- 紫色(Recalculate Style) や オレンジ色(Layout) が長く積み上がっている場所はないか?
- その直前に、自分が実行した `element.style.width = …` のようなJSが動いていないか?
「強制同期レイアウト(Forced Synchronous Layout)」というアンチパターンがある。JSでスタイルを書き換えた直後に `offsetHeight` 等を取得すると、ブラウザは溜まっていたレイアウト計算を「今すぐやれ!」と強制される。これがカクつきの元凶だ。
—
実践:パフォーマンスを最適化するコード設計
例えば、リストの要素を頻繁に動かしたい場合、どう書くのが正解か。以下のコードを見比べてほしい。
// 【アンチパターン】
// 読み取りと書き込みが交互に発生し、毎回レイアウト計算を強制している
const items = document.querySelectorAll(‘.list-item’);
items.forEach(item => {
const height = item.offsetHeight; // 読み取り(レイアウト発生!)
item.style.height = (height + 10) + ‘px’; // 書き込み(レイアウト発生!)
});
// 【ベストプラクティス】
// 読み取りを一括で済ませてから、書き込みを一括で行う(Batching)
const items = document.querySelectorAll(‘.list-item’);
// 1. 読み取りフェーズ
const heights = Array.from(items).map(item => item.offsetHeight);
// 2. 書き込みフェーズ
items.forEach((item, index) => {
item.style.height = (heights[index] + 10) + ‘px’;
});
なぜこれが速いのか?
ブラウザは本来、タスクの最後にまとめてレイアウト計算を行おうとする(これを「レイアウトの遅延評価」と呼ぶ)。上記のアンチパターンでは、JSが強制的にその列を割り込ませているんだ。読み取りと書き込みを分けるだけで、ブラウザが最適化する余地を確保できる。
—
現場のチーフアーキテクトからのアドバイス
最後に、一つだけ覚えて帰ってくれ。
「レイアウトをトリガーするプロパティを避ける」
`top`, `left`, `width`, `height` は避ける。代わりに `transform: translate(…)` や `scale()` を使う。これらはレイアウトのパイプラインを飛ばして、GPUの得意な「コンポジット」だけで処理してくれる。
Webブラウザは非常に賢い。だが、我々エンジニアの書き方次第で、その賢さを殺すこともできれば、最大限に引き出すこともできる。
「なんとなく動く」から「仕組みを理解して制御する」へ。一段上のエンジニアを目指すなら、まずは今書いているその `style` の操作が、ブラウザのパイプラインのどこを叩いているか、想像することから始めてみてほしい。
現場からは以上だ。また何かあればいつでも聞いてくれ。

コメント