【実務・中級編】 レイヤー爆発(Layer Explosion)の回避 – Webブラウザの仕組み実践ガイド

フロントエンドの現場で頭を悩ませるパフォーマンス問題の一つに、「なぜかスクロールがカクつく」「メモリ消費量が跳ね上がり、最悪の場合はタブがクラッシュする」という現象があります。

大体の原因をデベロッパーツールで追っていくと、決まって犯人はこいつです。そう、「レイヤー爆発(Layer Explosion)」。

今回は、ブラウザの描画エンジンの裏側で何が起きているのかという根本的な仕組みから、なぜレイヤーが爆発するのか、そしてそれをどうやってスマートに鎮火させるのかについて、現場の知見をたっぷり交えて解説していこう。

—

1. ブラウザの裏側で何が起きているのか? DOMからコンポジットまでの道のり

まずは、ブラウザがHTMLを読み込んで画面にピクセルを描画するまでのプロセスを軽くおさらいしておこう。ここを理解していないと、レイヤー爆発の本当の恐ろしさは見えてこない。

ブラウザのレンダリングエンジン(WebKitやBlinkなど)は、大まかに以下のステップを踏んで画面を構築する。

1. HTML/CSSのパース: HTMLからDOMツリーを作り、CSSからCSSOMツリーを作る。
2. スタイル計算 (Recalculate Style): DOMとCSSOMを合体させて、どの要素にどんなスタイルが適用されるかを決定する。
3. レイアウト (Layout / Reflow): 各要素が画面上のどこに、どれくらいのサイズで配置されるかを計算する。
4. ペイント (Paint / Rasterize): 「文字はここ、背景色はこれ」といった描画命令のリスト(レコード)を作り、それを実際のピクセル(ビットマップ)に変換(ラスタライズ)する。
5. コンポジット (Composite): 複数のレイヤーを重ね合わせて、最終的な画面をGPU上で合成する。

ここで重要なのが、最後の「コンポジット」だ。
モダンブラウザは、アニメーションのパフォーマンスを上げるためや、描画の効率化のために、特定の条件を満たした要素をメインの描画キャンバスから切り離し、独立した「GPUレイヤー(合成レイヤー)」として昇格させる。

GPUレイヤーの何が素晴らしいかといえば、位置(`transform: translate()`)や透明度(`opacity`)を変更する際、メインのレイアウトやペイントの処理をスキップして、GPUのハードウェアアクセラレーションだけでゴリゴリ動かせる点だ。60fps(あるいは120fps)の滑らかなアニメーションは、こいつのおかげで成り立っている。

—

2. 「レイヤー爆発」のメカニズム:なぜGPUが悲鳴を上げるのか?

「おっ、GPUレイヤーってことは、全部レイヤーにしちまえば爆速になるんじゃね?」
――もし過去にそう考えて、片っ端から `will-change: transform` や `transform: translateZ(0)` を書きまくったことがあるなら、今すぐその手を止めよう。それこそが、レイヤー爆発の引き金だ。

レイヤー爆発を引き起こすトリガー

  • `will-change: transform` や `will-change: opacity` の乱用
  • 3D系CSSプロパティ(`transform: translate3d()` や `perspective`)の無計画な多用
  • `z-index` のコンテキストが複雑に絡み合った状態で、重なり合う多数の要素にレイヤー化ヒントを与えた場合
  • `