【実務・中級編】 メインスレッドとコンポジタスレッド – Webブラウザの仕組み実践ガイド

やあ。今日は少し「ブラウザの深淵」の話をしようか。

君たちが普段書いているJavaScriptやCSSが、ブラウザという名の巨大なエンジンの中でどう扱われているか。これを知っているかどうかで、パフォーマンスチューニングの「打率」は劇的に変わる。

特に「メインスレッド」と「コンポジタスレッド」の役割分担は、モダンなWeb開発における生存戦略そのものだ。今日はここを深掘りする。

—

1. メインスレッドは「設計者」、コンポジタは「現場監督」

まず、誤解を解こう。ブラウザは単にコードを上から下へ流し込んでいるわけじゃない。

  • メインスレッド(Main Thread): ここは「頭脳」だ。HTMLをパースし、DOMを作り、CSSを当ててスタイルを計算し、レイアウト(位置)を決め、JavaScriptを実行する。いわば、すべての設計図を書く職人だよ。
  • コンポジタスレッド(Compositor Thread): ここは「現場監督」だ。メインスレッドが描いた「設計図(レイヤー)」を、実際に画面のピクセルとして合成する。GPUを使いこなすのが得意な、非常に手際の良いやつさ。

なぜこの分離が重要か? 理由はシンプルだ。メインスレッドがJavaScriptで詰まると、ページ全体が「フリーズ」するからだ。 一方、コンポジタスレッドはメインスレッドが忙しくても、既に持っている情報を元に画面をスクロールさせたり、アニメーションさせたりできる。これが「ヌルヌル動く」UIの正体だ。

—

2. なぜ「レイアウト」が重いのか?

現場でよくあるミスは、メインスレッドに過度な負担をかけることだ。CSSのプロパティには「メインスレッドを動かさないと計算できないもの」と「コンポジタだけで完結するもの」がある。

例えば、`top` や `left` を動かすと、ブラウザは「レイアウト(再計算)」と「ペイント(描画)」の工程をメインスレッドでやり直す。これが16.6ms(60fps)を超えると、ユーザーは「カクつき」を感じる。

一方、`transform` や `opacity` は、メインスレッドをスキップしてコンポジタスレッドだけで処理を完了できる(これを「Compositor-only properties」と呼ぶ)。

—

3. 実践:メインスレッドを解放する「賢いアニメーション」

理屈はわかっただろう。では、コードでどう差をつけるか。以下のコードを見比べてほしい。

/

  • アンチパターン:メインスレッドを酷使するアニメーション
  • ‘top’ を変更すると、毎回「レイアウト」が発生し、メインスレッドが泣き叫ぶ。

/
function animateBad(element) {
let pos = 0;
const frame = () => {
pos++;
element.style.top = pos + ‘px’; // ここでメインスレッドに重い負荷がかかる
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
}

/

  • ベストプラクティス:コンポジタスレッドに任せる
  • ‘transform’ を使うことで、ブラウザはGPUレイヤーを直接操作し、
  • メインスレッドがJSの計算で忙しくてもアニメーションを維持できる。

/
function animateGood(element) {
let pos = 0;
const frame = () => {
pos++;
// transformなら描画エンジンが最適化してくれる
element.style.transform = `translateY(${pos}px)`;
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
}

ポイントは、「CSSのどのプロパティがブラウザのどの工程を呼び出すか」を意識することだ。`will-change` プロパティも強力だが、乱用は禁物だ。メモリを食い潰すから、必要な要素にだけピンポイントで使うのがプロの流儀だ。

—

4. 現場のエンジニアへ送るアドバイス

君たちが普段の開発で意識すべきは、「メインスレッドにどれだけ暇を与えられるか」だ。

1. 重い計算はWeb Workersに逃がす: JSの計算が数ミリ秒を超えるなら、迷わずWorkerへ飛ばせ。DOMに直接触れられないという制約はあるが、メインスレッドを空けるメリットは計り知れない。
2. `requestAnimationFrame` を守護神にする: `setTimeout` や `setInterval` でDOMを操作してはいけない。それらはフレームのタイミングを無視するから、ガクつく原因になる。
3. Chrome DevToolsの「Layers」タブを見る: どの要素が独自のレイヤーを持っているか可視化してみろ。無駄にレイヤーが増えすぎると、今度は合成コストが高くなる。バランスが大事だ。

結局のところ、ブラウザの仕組みを理解するというのは、「ブラウザがいかに楽をして画面を出せるか」を助けてやる作業なんだ。

今日話した内容は、ブラウザという黒い箱の中身を覗く最初の一歩に過ぎない。次は「ブラウザがどうやってスクロールを最適化しているか」について深掘りしてみるのも面白いだろう。

現場からは以上だ。また何かあればいつでも聞きに来てくれ。君のコードが、より滑らかに動くことを期待しているよ。

コメント

タイトルとURLをコピーしました