「このサイト、表示が遅い…」 「スクロールがカクカクする!」 「ボタンを押しても反応しない…」
こんな経験、ありませんか?
実は、これらの問題の多くはレンダリングが原因なんです。 レンダリングとは、HTMLやCSSといったコードを、私たちが見る美しいWebページに変換する過程のこと。
この記事を読めば、ブラウザがどうやって画面を描いているのか、なぜ遅くなるのか、どうすれば速くできるのか、すべて分かるようになります!
レンダリングを5秒で理解!料理に例えると

レンダリング = レシピから料理を作ること
【料理の場合】
レシピ(文字)→ 調理 → 完成した料理(見た目)
【Webページの場合】
HTML/CSS(コード)→ レンダリング → 画面表示(見た目)
具体的な流れ:
1. レシピを読む = HTMLを解析
2. 材料を準備 = CSSやJavaScriptを読み込み
3. 下ごしらえ = DOMツリーを構築
4. 調理する = レイアウト計算
5. 盛り付け = 画面に描画
ブラウザは、この工程を1秒間に60回も繰り返して、滑らかな動きを実現しているんです!
ブラウザレンダリングの仕組み:5つのステップ
ステップ1:パース(解析)
HTMLパース → DOMツリー作成
<!-- HTML -->
<div>
<h1>タイトル</h1>
<p>本文</p>
</div>
<!-- DOMツリー -->
div
├─ h1
│ └─ "タイトル"
└─ p
└─ "本文"
CSSパース → CSSOMツリー作成
/* CSS */
div { color: blue; }
h1 { font-size: 24px; }
/* CSSOMツリー */
スタイルルールを木構造で管理
ステップ2:レンダーツリー構築
DOMツリー + CSSOMツリー = レンダーツリー
表示される要素だけを含む
(display: noneは除外)
ステップ3:レイアウト(リフロー)
各要素の位置とサイズを計算
例:
・divの幅は親要素の100%
・h1は上から20px
・pはh1の下に配置
ステップ4:ペイント
ピクセル単位で色を決定
・背景色を塗る
・文字を描く
・画像を配置
・影を追加
ステップ5:コンポジット(合成)
レイヤーを重ねて最終画面を作成
レイヤー1:背景
レイヤー2:メインコンテンツ
レイヤー3:固定ヘッダー
↓
合成して1枚の画面に!
レンダリング方式の種類:CSR vs SSR vs SSG
1. CSR(Client-Side Rendering)- ブラウザで描画
【仕組み】
1. 空のHTMLを受信
2. JavaScriptをダウンロード
3. JavaScriptが実行
4. DOMを動的に生成
5. 画面に表示
【特徴】
初回表示:遅い(3-5秒)
ページ遷移:速い(0.1秒)
SEO:弱い
使用例:Gmail、Twitter、Facebook
2. SSR(Server-Side Rendering)- サーバーで描画
【仕組み】
1. サーバーでHTML生成
2. 完成したHTMLを送信
3. ブラウザが即座に表示
4. JavaScriptで機能追加
【特徴】
初回表示:速い(1-2秒)
ページ遷移:普通(0.5秒)
SEO:強い
使用例:ニュースサイト、ECサイト
3. SSG(Static Site Generation)- 事前に生成
【仕組み】
1. ビルド時にHTML生成
2. CDNに配置
3. 超高速配信
【特徴】
初回表示:最速(0.5秒)
更新:ビルドが必要
SEO:最強
使用例:ブログ、ドキュメント、企業サイト
比較表
方式 | 初回表示 | インタラクティブ性 | SEO | サーバー負荷 |
---|---|---|---|---|
CSR | ✗遅い | ◎高い | ✗弱い | ◎軽い |
SSR | ○速い | ○普通 | ◎強い | ✗重い |
SSG | ◎最速 | △低い | ◎最強 | ◎最軽 |
ISR | ◎速い | ○普通 | ◎強い | ○軽い |
パフォーマンスキラー:レンダリングを遅くする原因TOP5

1. 巨大なDOM
【悪い例】
<div>
<div>
<div>
<div>
<!-- 無駄にネストが深い -->
</div>
</div>
</div>
</div>
【良い例】
<div class="container">
<!-- シンプルな構造 -->
</div>
影響:DOMが10,000ノード超えると激遅!
2. レイアウトスラッシング
// 悪い例:読み書きを交互に
for (let i = 0; i < 100; i++) {
element.style.left = element.offsetLeft + 10 + 'px';
// offsetLeftで読み → styleで書き → リフロー発生!
}
// 良い例:まとめて処理
const currentLeft = element.offsetLeft;
for (let i = 0; i < 100; i++) {
// 計算だけ
}
element.style.left = newPosition + 'px';
3. 重いCSS
/* 悪い例:全称セレクタ */
* {
box-shadow: 0 0 10px rgba(0,0,0,0.5);
}
/* 悪い例:深いセレクタ */
div > ul > li > a > span {
color: red;
}
/* 良い例:クラスで直接指定 */
.menu-item {
color: red;
}
4. 画像の最適化不足
<!-- 悪い例:巨大な画像 -->
<img src="photo.jpg" width="200">
<!-- 5000×3000pxを200pxで表示... -->
<!-- 良い例:適切なサイズ -->
<img src="photo-200w.jpg"
srcset="photo-200w.jpg 200w,
photo-400w.jpg 400w"
sizes="200px">
5. JavaScriptの実行
// 悪い例:メインスレッドをブロック
for (let i = 0; i < 1000000; i++) {
// 重い処理
}
// 良い例:Web Workerで別スレッド
const worker = new Worker('heavy-task.js');
worker.postMessage({cmd: 'start'});
レンダリング最適化テクニック15選
基本的な最適化
1. Critical CSS をインライン化
<head>
<style>
/* ファーストビューのCSSだけ */
.header { background: blue; }
.hero { height: 100vh; }
</style>
<!-- 残りは遅延読み込み -->
<link rel="preload" href="style.css" as="style">
</head>
2. JavaScript を非同期化
<!-- ブロッキング -->
<script src="app.js"></script>
<!-- 非ブロッキング -->
<script async src="analytics.js"></script>
<script defer src="app.js"></script>
3. フォントの最適化
@font-face {
font-family: 'MyFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* すぐに表示 */
}
高度な最適化
4. 仮想スクロール
// 1万件のリストでも表示は50件だけ
const visibleItems = allItems.slice(
scrollPosition,
scrollPosition + 50
);
5. Intersection Observer で遅延読み込み
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 画面に入ったら読み込み
const img = entry.target;
img.src = img.dataset.src;
}
});
});
6. CSS Containment
.card {
contain: layout style paint;
/* この要素の変更が他に影響しない */
}
7. will-change でGPU最適化
.animation {
will-change: transform;
/* GPUレイヤーに移動 */
}
React/Vue での最適化
8. React.memo で再レンダリング防止
const ExpensiveComponent = React.memo(({ data }) => {
// dataが変わらなければ再レンダリングしない
return <div>{data}</div>;
});
9. Vue の v-show vs v-if
<!-- 頻繁に切り替え → v-show(display: none)-->
<div v-show="isVisible">コンテンツ</div>
<!-- たまに切り替え → v-if(DOM削除)-->
<div v-if="isLoggedIn">ダッシュボード</div>
10. キーの適切な使用
// 悪い:インデックスをキーに
items.map((item, index) => <li key={index}>{item}</li>)
// 良い:一意のIDをキーに
items.map(item => <li key={item.id}>{item.name}</li>)
画像最適化
11. 次世代フォーマット
<picture>
<source type="image/webp" srcset="image.webp">
<source type="image/jpeg" srcset="image.jpg">
<img src="image.jpg" alt="説明">
</picture>
12. レスポンシブ画像
<img srcset="small.jpg 480w,
medium.jpg 800w,
large.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px"
src="medium.jpg">
ネットワーク最適化
13. Resource Hints
<!-- DNS先読み -->
<link rel="dns-prefetch" href="//api.example.com">
<!-- 接続の事前確立 -->
<link rel="preconnect" href="//fonts.googleapis.com">
<!-- リソースの先読み -->
<link rel="prefetch" href="next-page.js">
<!-- 重要リソースの事前読み込み -->
<link rel="preload" href="critical.css" as="style">
14. Service Worker でキャッシュ
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
15. HTTP/2 プッシュ
Link: </styles.css>; rel=preload; as=style
Core Web Vitals:Googleが重視する3つの指標

1. LCP(Largest Contentful Paint)- 読み込み速度
最大コンテンツの表示時間
目標:2.5秒以内
改善方法:
・画像の最適化
・Critical CSSの利用
・CDNの活用
2. FID(First Input Delay)- インタラクティブ性
最初の入力遅延
目標:100ミリ秒以内
改善方法:
・JavaScript分割
・Web Worker活用
・メインスレッドの最適化
3. CLS(Cumulative Layout Shift)- 視覚的安定性
レイアウトのズレ
目標:0.1以下
改善方法:
・画像サイズ指定
・動的コンテンツの予約
・フォント読み込み最適化
デバッグツール:レンダリング問題を見つける
Chrome DevTools の活用
1. Performance タブ
記録開始 → ページ操作 → 記録停止
確認ポイント:
・赤いバー = 長いタスク(50ms以上)
・紫 = レンダリング
・黄色 = JavaScript
・青 = 解析
2. Rendering タブ
設定で表示:
□ Paint flashing(再描画領域を緑で表示)
□ Layer borders(レイヤー境界を表示)
□ FPS meter(フレームレート表示)
3. Coverage タブ
使われていないCSS/JSを発見
赤い部分 = 未使用コード
削減の目安:
50%以上未使用 → 分割を検討
Lighthouse での測定
# CLIで実行
npm install -g lighthouse
lighthouse https://example.com --view
# 確認項目
・Performance スコア
・各メトリクスの詳細
・改善提案
実践的なケーススタディ

ケース1:ニュースサイトの高速化
問題:
- 初回表示に5秒
- 画像が多くて重い
- 広告でレイアウトがずれる
解決策:
// 1. 画像の遅延読み込み
<img loading="lazy" src="news.jpg">
// 2. 広告スペースの予約
.ad-container {
min-height: 250px; /* 広告の高さを予約 */
}
// 3. Critical CSS の分離
// ファーストビューのCSSだけインライン化
結果:
- LCP: 5秒 → 2秒
- CLS: 0.25 → 0.05
- 直帰率: 40% → 20%
ケース2:ECサイトの商品一覧
問題:
- 商品1000件表示でスクロールがカクカク
- フィルター適用が遅い
解決策:
// 1. 仮想スクロール実装
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={600}
itemCount={1000}
itemSize={100}
>
{ProductItem}
</FixedSizeList>
// 2. デバウンス処理
const debouncedFilter = debounce((value) => {
applyFilter(value);
}, 300);
結果:
- 60fps達成
- メモリ使用量80%削減
よくある質問(FAQ)
Q1. SPAは遅い?
初回表示は遅いが、その後は高速
対策:
・Code Splitting
・Prerendering
・Progressive Enhancement
Q2. jQuery は使わない方がいい?
現代のブラウザなら不要なことが多い
// jQuery
$('#element').addClass('active');
// Vanilla JS(十分速い)
document.getElementById('element').classList.add('active');
Q3. アニメーションで注意することは?
/* 悪い:レイアウトを変更 */
.animate {
left: 100px;
width: 200px;
}
/* 良い:transformとopacityのみ */
.animate {
transform: translateX(100px);
opacity: 0.8;
}
Q4. PWAにすべき?
メリット:
・オフライン対応
・プッシュ通知
・アプリライク
デメリット:
・実装コスト
・iOS制限
→ ユーザー体験重視なら検討価値あり
レンダリングの未来:新技術動向
1. React Server Components
// サーバーでのみ実行
async function ServerComponent() {
const data = await fetchData(); // サーバーで取得
return <div>{data}</div>;
}
// バンドルサイズ削減
// Hydration不要
2. Streaming SSR
// 段階的にHTMLを送信
res.write('<html><head>...');
res.write(await renderHeader());
res.write(await renderContent());
res.end('</html>');
3. WebGPU
・3D/2Dグラフィックスの高速化
・機械学習の実行
・2023年から利用可能
4. View Transitions API
/* ページ遷移をスムーズに */
::view-transition-old(root) {
animation: fade-out 0.3s;
}
::view-transition-new(root) {
animation: fade-in 0.3s;
}
まとめ:レンダリングを制する者はWebを制す!
レンダリングの理解は、高速なWebサイト作りの第一歩です。
重要ポイント:
- レンダリングパイプラインを理解する
- 適切な方式を選ぶ(CSR/SSR/SSG)
- パフォーマンスキラーを避ける
- Core Web Vitalsを意識する
- 継続的な計測と改善
今すぐできる改善:
- 画像の遅延読み込み
- JavaScriptの非同期化
- Critical CSSの分離
- 不要なリソースの削除
投資する価値があるもの:
- CDNの導入
- 画像最適化ツール
- パフォーマンス監視ツール
- SSR/SSGフレームワーク
レンダリングを最適化すれば、ユーザー体験は劇的に向上します! まずはLighthouseで現状を測定してみましょう。
高速なWebの世界へ、ようこそ!
コメント