【実務・中級編】 コンポジター・スレッドによる並列処理 – Webブラウザの仕組み実践ガイド

なぜ「ブラウザのスクロール」は、JSで重い処理をしてもカクつかないのか?

フロントエンドの現場で「レンダリングが重い」という相談を受けるとき、多くのエンジニアが真っ先に疑うのは `requestAnimationFrame` のループや、膨大なDOM操作です。確かにそれらはメインスレッドを殺す最大の要因ですが、「なぜスクロールやCSSアニメーションだけは、メインスレッドが死んでいても滑らかに動くのか?」という問いに即答できるでしょうか。

ここを理解しているかどうかで、パフォーマンスチューニングの「打率」は劇的に変わります。今日は、ブラウザの影の立役者「コンポジター・スレッド」の話をしましょう。

—

ブラウザの裏側:メインスレッド vs コンポジター・スレッド

ブラウザは単一の巨大なループではありません。特にモダンなブラウザ(Chromium系など)は、責務を細かく分割しています。

  • メインスレッド(Main Thread): JSの実行、DOMの構築(Parse)、CSSの計算(Recalculate Style)、レイアウト(Layout)といった「計算」を担う場所。いわば脳みそです。
  • コンポジター・スレッド(Compositor Thread): 画面上の「絵」を統合(Composite)する役割。スクロールイベントを受け取り、GPUに「ここを動かせ」と命令を送る、いわば現場監督です。

なぜこれが重要なのか?
もしスクロールの処理までメインスレッドが握っていたら、ページ内で `while(true)` のような重い計算が走った瞬間にスクロールが停止し、ユーザーは「フリーズした」と感じます。しかし、コンポジターはメインスレッドから「この層(Layer)はここにあるよ」という地図だけ先に受け取っておくため、メインスレッドが死んでいても、コンポジターは「すでに持っている地図」をずらすだけでスクロールを実現できるのです。

これが、「並列処理」が生む魔法の正体です。

—

コンポジターの力を引き出す:`will-change` の功罪

コンポジター・スレッドに処理を任せるには、その要素を「別のレイヤー(Composited Layer)」として独立させる必要があります。昔は `transform: translateZ(0)` という呪文が流行りましたが、今はもっとスマートに書きましょう。

.scroll-optimized-element {
/ ブラウザに「この要素は変化するから、前もって独立したレイヤーにしといてくれ」と伝える /
will-change: transform;

/ レイヤー化を明示的に強制するCSSプロパティ /
/ ただし、使いすぎるとメモリを食いつぶすので注意。必要な場所にだけ貼るのがプロの作法 /
}

注意: 「とりあえず全部に `will-change` を貼れば爆速になる」と勘違いしているジュニアエンジニアがいますが、それは違います。レイヤーが増えるということは、ブラウザがメモリを大量に消費し、GPUへの転送コストも増大するということです。「本当に動きが激しい部分」だけにピンポイントで投与するのが、現場のベストプラクティスです。

—

実践:メインスレッドをブロックしつつ、アニメーションを維持する

実際に、メインスレッドをわざと止めても、コンポジターが動いていれば滑らかさが維持できることを確認する実験的なコードです。

/

  • メインスレッドを意図的にブロックする関数
  • @param {number} ms – ブロックする時間

/
function blockMainThread(ms) {
const start = Date.now();
while (Date.now() – start < ms) { // ここでJSが重い処理をしていると仮定 } } // 画面上の特定の要素に対して、CSSの transform でアニメーションさせる const box = document.querySelector('.box'); // ユーザーがボタンを押した瞬間にメインスレッドを5秒間止める document.querySelector('#trigger').addEventListener('click', () => {
console.log(‘メインスレッドを止めるよ…’);

// これを実行しても、CSSアニメーションは止まりません!
// なぜなら、コンポジター・スレッドが独立してGPUで処理しているから。
blockMainThread(5000);

console.log(‘メインスレッド復帰!’);
});

このコードをブラウザで動かしてボタンを押してみてください。JSの実行中はコンソールに何も表示されず、ブラウザ全体が反応しなくなりますが、CSSで定義されたアニメーションだけは滑らかに動き続けます。これが「コンポジター・スレッドの独立性」です。

—

シニアからのアドバイス:レンダリング・パイプラインを意識せよ

エンジニアとして成長するなら、常に以下の「レンダリング・パイプライン」を頭に浮かべてください。

1. JavaScript: 変更を加える
2. Style: CSSの再計算
3. Layout: どの要素がどこにあるか計算(重い!)
4. Paint: 各ピクセルの描画(重い!)
5. Composite: レイヤーの合成(軽い!)

「Layout」や「Paint」を避けて、直接「Composite」だけで完結するプロパティ(`transform` や `opacity`)を使うこと。 これが、ブラウザのアーキテクチャを理解した人間が書く「高速なUI」の鉄則です。

何かを動かしたいとき、`top` や `left` を操作していませんか? それはパイプラインの最初からやり直しを強いる、最も非効率な手段です。`transform: translate()` を使えば、コンポジター・スレッドがニヤリと笑って、メインスレッドを介さずに高速処理してくれますよ。

フロントエンドのパフォーマンスとは、ブラウザという巨大な機械の「機嫌」を損ねないように、賢く仕事を振る技術のことです。ぜひ、今日から意識してみてください。

コメント

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