【テクニカル・上級編】 メインスレッドとコンポジタスレッド – Webブラウザの仕組み実践ガイド

レンダリングの「心臓部」を理解する:メインスレッドとコンポジタスレッドの静かなる共闘

Web開発の現場で、私たちは日々DOMを操作し、CSSを当てていますが、ブラウザの内部で何が起きているのかを「肌感覚」で理解しているエンジニアは驚くほど少ない。

特に、ブラウザがどのようにピクセルを画面に叩き込んでいるのか。その全権を握る「メインスレッド」と、現代の滑らかなUIを支える「コンポジタスレッド」の役割分担を理解することは、上級エンジニアへの登竜門です。今日は、この二つのスレッドがどのように連携し、時には激しく衝突し、どのようにして「ヌルヌル動くWeb」を実現しているのか、その深淵を覗いてみましょう。

—

メインスレッド:責任の重すぎる「孤高の芸術家」

メインスレッドは、ブラウザの「頭脳」です。JavaScriptの実行、HTMLのパース、DOMの構築、CSSOMの計算、そしてLayout(Reflow)とPaintの準備。これら全てを一人でこなします。

問題は、「JavaScriptの実行が止まれば、画面の更新も全て止まる」という一点に尽きます。メインスレッドが重い計算やDOM操作に忙殺されている間、ユーザーがどれだけスクロールしても、画面はフリーズしたように見えます。これが「ジャンク(Jank)」の正体です。

コンポジタスレッド:UIを守る「機敏な仕事人」

一方、コンポジタスレッド(Compositor Thread)は、メインスレッドの混雑から解放された独立した存在です。彼らの主な仕事は、画面の「レイヤー」を合成し、GPUへ命令を送ること。

現代のブラウザは、ページを複数のレイヤー(`will-change`プロパティや`transform`などで生成されるもの)に分解しています。メインスレッドがJavaScriptで忙しくても、コンポジタスレッドは「前回と同じレイヤー配置を再利用して、GPUで描画し直す」という荒業が可能です。これこそが、スクロールやアニメーションが滑らかに見える物理的な理由です。

—

パフォーマンス最適化の極致:メインスレッドを「退屈」させる

上級エンジニアとしての戦略は明確です。「メインスレッドに触らせないこと」。

JavaScriptでアニメーションを書く際、`left`や`top`を操作してはいけません。これらはLayoutを誘発し、メインスレッドを強制的に働かせます。代わりに、コンポジタスレッドだけで完結する`transform`や`opacity`を使いましょう。

/

  • 悪い例: メインスレッドでLayoutを発生させ続ける
  • topを動かすと、ブラウザは毎回配置計算(Layout)をやり直す

/
function animateBad(element) {
let pos = 0;
function step() {
pos += 1;
element.style.top = pos + ‘px’; // 毎フレームLayoutを強制
requestAnimationFrame(step);
}
requestAnimationFrame(step);
}

/

  • 良い例: コンポジタスレッドに委譲する
  • transformはGPUレイヤーで合成されるため、メインスレッドは暇なまま

/
function animateGood(element) {
// CSS側で will-change: transform; を指定しておくのがベスト
element.style.transform = `translateY(${pos}px)`; // Layoutをスキップ
}

—

「非同期の競合」と知られざるバグ:強制同期レイアウトの罠

メインスレッドをいじめる最大の要因、それが「強制同期レイアウト(Forced Synchronous Layout)」です。

これは、JavaScriptでDOMのスタイルを「書き換えた直後」に、配置情報を「読み取る」ことで発生します。ブラウザは「最新のスタイルを適用しなければ正確な位置を答えられない」と判断し、本来後回しにできるLayoutをその場で実行させられます。

// 危険なパターン
const box = document.getElementById(‘box’);
for (let i = 0; i < 100; i++) { box.style.width = i + 'px'; // 書き込み console.log(box.offsetWidth); // 読み取り -> ここで強制同期レイアウトが発生!
}

このコードを実行すると、ブラウザは100回連続でLayoutを強要されます。これを避けるには、「読み取り」と「書き込み」を完全に分離するバッチ処理のアーキテクチャが必要です。

—

伝説的アーキテクトからの助言

最後に、現場で生き残るための鉄則を伝えておきます。

1. DOM操作は最小単位にせよ: DOMツリーの変更は、可能な限り一箇所に集約し、`DocumentFragment`や仮想DOM的なアプローチで更新頻度を抑えること。
2. レイヤーの乱用を避けろ: `will-change`は魔法の杖ではありません。多用しすぎるとGPUメモリを食いつぶし、逆にブラウザをクラッシュさせます。
3. プロファイラは嘘をつかない: Chrome DevToolsの「Performance」タブを見なさい。メインスレッドが埋め尽くされている「赤いバー」を見つけたら、それがあなたのコードの罪の証拠です。

ブラウザは優秀ですが、万能ではありません。彼らが何を考え、どう動こうとしているのか。その背中を理解してあげることこそが、最高峰のWebアプリケーションを構築するための唯一の近道です。

さあ、コードを開いて、あなたのメインスレッドを解放してあげてください。きっと、ブラウザは驚くほど軽快なレスポンスで応えてくれるはずです。

コメント

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