19
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Frontend Performance - Part 4] 描画パフォーマンス最適化:ブラウザの仕事を減らす設計とは?

19
Last updated at Posted at 2026-04-22

ChatGPT Image Apr 22, 2026, 01_28_51 PM.png


📝 注意
本記事はAIの補助を受けて編集しています。
内容は大規模Webアプリケーションの実務経験に基づいています。


📚 目次


0. はじめに

これまでのシリーズでは、ブラウザの内部動作を段階的に見てきました。

  • Part 1: Rendering Pipeline(描画の流れ)
  • Part 2: Reflow / Repaint / Composite(コストの違い)
  • Part 3: Critical Rendering Path(初期表示が遅くなる理由)

ここまでで分かることは、とてもシンプルです。

ブラウザは想像以上に多くの処理をしている

それにもかかわらず、開発現場ではこう考えがちです。

  • 「とりあえず JS を軽くすればいい」
  • memo 化すれば速くなるはず」

しかし、本質はそこではありません。

本質

❌ 「どうやって速くするか」ではなく
「ブラウザに余計な仕事をさせないか」

本記事では、描画パフォーマンス最適化の設計思想を解説します。


1. ブラウザの仕事を減らすとは?

ブラウザは 1 フレーム(約 16ms) ごとに、以下の処理を行っています。

  • Layout(リフロー):位置・サイズの計算
  • Paint(リペイント):ピクセルの描画
  • Composite(コンポジット):レイヤーの合成

この処理が 16ms を超えると

フレーム落ち(jank) が発生し、カクつきの原因になります。


重要な視点

パフォーマンス改善とは:

  • ❌ 処理を「速くする」ことではない
  • 処理そのものを減らすこと

基本原則

NG(避けるべき) OK(推奨)
レイアウトを頻繁に発生させる 更新をまとめて行う(バッチ化)
DOM を何度も細かく更新する 一括更新する(class 切替など)
画面全体を再描画させる レイヤーで影響範囲を限定
top / left でアニメーション transform を使う

2. レイアウト計算を減らす(Reflow 削減)

Reflow は最もコストが高い

Reflow が発生すると、ブラウザは以下を再計算します:

  • DOM 構造
  • CSS の適用結果
  • 要素の位置・サイズ

影響範囲が広く、最も重い処理


よくある NG:Layout Thrashing

JavaScript で 読み取り(read)と書き込み(write)を交互に行うと、
毎回強制的に Reflow が発生します。

// ❌ 悪い例
element.style.width = '100px';     // write
console.log(element.offsetWidth);  // read → 強制リフロー

改善方法

1. 読み取りと書き込みを分離

const width = element.offsetWidth;
const height = element.offsetHeight;

element.style.width = width + 100 + 'px';
element.style.height = height + 100 + 'px';

2. DOM 更新はまとめる

// ❌ バラバラ更新
el.style.width = '100px';
el.style.height = '100px';

// ✅ 一括更新
el.style.cssText = 'width:100px;height:100px;';

3. CSS Containment を使う

.card {
  contain: layout;
}

レイアウトの影響範囲を限定できる


3. ペイントコストを下げる(Repaint 最適化)

Repaint は「見た目の再描画」です。


重いプロパティ

  • box-shadow
  • border-radius
  • filter
  • gradient
  • position: fixed

広範囲に使うと負荷が大きい


対策

1. レイヤー分離

.animated {
  will-change: transform;
}

2. 重い装飾を避ける

/* ❌ */
box-shadow: 0 0 20px rgba(0,0,0,0.5);

/* ✅ */
border-bottom: 1px solid rgba(0,0,0,0.1);

4. Composite だけで済ませる設計(最重要)

理想

Layout も Paint も発生させない

Composite のみで処理


GPU で処理できるプロパティ

  • transform
  • opacity

NG / OK

/* ❌ */
left: 100px;

/* ✅ */
transform: translateX(100px);

効果

  • GPU で処理される
  • メインスレッドをブロックしない
  • 60fps を維持しやすい

5. レイヤー設計と GPU 活用

ブラウザは レイヤー単位で描画します。


レイヤー化

.animated {
  will-change: transform;
}

注意

  • レイヤーはメモリを消費する
  • 作りすぎると逆効果

6. DOM 構造とレンダリングコスト

DOM が大きいと遅くなる理由

  • Layout 計算量が増える
  • Paint 範囲が広がる

対策

1. フラットな構造

2. 仮想化(Virtualization)

必要な分だけ DOM を生成

3. コンポーネント分離

再レンダリング範囲を限定


7. JavaScript との関係(軽く)

JavaScript 実行中は描画が止まります。


悪い例

for (let i = 0; i < 1000000000; i++) {}

対策

  • requestAnimationFrame
  • debounce / throttle
  • タスク分割

8. まとめ

Key Takeaways

  • パフォーマンスは設計で決まる
  • 「速くする」ではなく 「無駄な処理をさせない」

優先順位:

  1. Composite だけで済ませる
  2. Paint を減らす
  3. Layout を減らす

💡 良い設計とは、ブラウザに無駄な仕事をさせないこと


👉 次回予告
[Frontend Performance - Part 5] なぜ UI は止まるのか?JavaScript の実行モデルと Event Loop を理解する

19
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?