ブラウザの「悲鳴」を可視化せよ:DevTools Performance解析によるメインスレッド最適化の極意
多くのエンジニアが「なんとなく重い」という感覚でWebサイトを放置している。しかし、ユーザーが離脱するのは「感覚」ではなく「物理的なメインスレッドの占有」が原因だ。
Chrome DevToolsのPerformanceタブは、単なるグラフ表示ツールではない。これは、ブラウザという限られたリソースの中で、JavaScriptという「独裁者」がどれだけメインスレッドを占拠しているかを暴くための法医学ツールである。
本稿では、メインスレッドのブロックを特定し、ボトルネックを「数値」として叩き出し、コードを外科手術のように最適化するまでのプロの作法を伝授する。
—
1. なぜ「Main」スレッドの可視化がすべてなのか
ブラウザのメインスレッドは、JS実行、リフロー、リペイントを一手に行う「唯一の心臓」だ。ここが50msを超えてロックされると、ユーザー入力への応答が遅れる(Input Delay)。
隠れたキーボードショートカット:解析の初動を加速する
マウスでクリックしている暇はない。以下のショートカットで計測を自動化せよ。
- `Ctrl + Shift + E` (Mac: `Cmd + Option + E`): 記録の開始・終了。
- `Esc`: Drawer(コンソール等)の開閉。特に、`Rendering` タブをDrawerに出しておき、「Paint Flashing」を常時ONにしておくのが鉄則だ。不要な再描画箇所が緑色に光るため、JSの実行がどのDOMに影響を与えているか一目で判別できる。
—
2. Long Taskの正体を暴く外科手術の手順
「赤い三角」がついたタスクをただ眺めても意味はない。以下のステップで解剖を行う。
Step 1: Bottom-Up タブで「Self Time」をソートする
`Total Time` ではなく、`Self Time` を見よ。`Total Time` は親関数の影響を受けるが、`Self Time` は「その関数そのものが実行に費やした時間」を示す。ここが肥大化している関数こそが、真の犯人だ。
Step 2: Call Tree で「誰が呼んだか」を追う
`Self Time` が長い関数の親を辿れ。多くの場合、Reactの `useEffect` や、サードパーティの計測タグ(GTM経由のスクリプトなど)が犯人だ。
Step 3: レンダリングの「連鎖」を断つ
`Event (click)` -> `Function A` -> `Function B` -> `Recalculate Style` と続いている場合、Bの中でDOMの読み取りと書き込みを交互に行う「強制同期レイアウト」が発生している可能性が高い。
—
3. チームの生産性を底上げする「設定共有化」のプロトコル
個人のPC内だけで完結する最適化は、技術的負債を再生産する。DevToolsの設定を「チームの共通言語」にする必要がある。
推奨:`.devtools` 設定の運用とルール
DevToolsの「Experiments」や「Settings」はエクスポートできないが、「パフォーマンス計測の定義」を共有するドキュメントはCI/CDパイプラインの一部として組み込むべきだ。
以下は、`lighthouserc.json` を活用した、チーム開発における「パフォーマンス・ゲート」の構成例である。
{
“ci”: {
“collect”: {
“numberOfRuns”: 3,
“url”: [“https://staging.example.com”],
“settings”: {
“cpuSlowdownMultiplier”: 4 // 低スペック環境をシミュレートする重要設定
}
},
“assert”: {
“preset”: “lighthouse:recommended”,
“assertions”: {
“long-tasks”: [“error”, {“maxNumericValue”: 50}], // 50ms以上のタスクを検出したらビルドを落とす
“mainthread-work-breakdown”: [“warn”, {“maxNumericValue”: 2000}]
}
}
}
}
この設定の狙い:
`cpuSlowdownMultiplier` を4に設定することで、開発者のハイエンドMacではなく、実世界のユーザー層に近い「中堅Android端末」の速度を強制的に再現する。開発者のマシンで動くから良い、という甘えを排除するのがテックリードの仕事だ。
—
4. 現場で震えるほど役立つ「プロのTips」
1. `performance.mark()` と `performance.measure()` の活用
DevToolsに表示されるタスク名は難解なMinify後の名前であることが多い。コード内に以下を仕込め。
performance.mark(‘my-heavy-task-start’);
// 重い処理
doExpensiveCalculation();
performance.mark(‘my-heavy-task-end’);
performance.measure(‘my-heavy-task’, ‘my-heavy-task-start’, ‘my-heavy-task-end’);
こうすることで、Performanceタブのタイムライン上に自分の命名したタスクが明確なバーとして表示されるようになる。
2. 「Rendering」タブの「Layer Borders」
これをONにすると、GPUで合成されているレイヤーがオレンジ色の枠で囲まれる。JSをいじって「重い」と感じる原因が、実はCSSの `will-change` の乱用によるメモリリークだった、というケースを即座に特定できる。
—
結論:計測は「意思」である
ブラウザのメインスレッドを読み解くことは、ユーザーの時間を守ることに直結する。
1. 計測: `cpuSlowdownMultiplier` を使い、常に「最悪の環境」を想定せよ。
2. 特定: `Self Time` で犯人を特定し、`performance.mark` で自分のコードに名前を刻め。
3. 自動化: `lighthouserc.json` で、パフォーマンス低下を「ビルドエラー」として検知できる仕組みを作れ。
ツールを使いこなすことは、単なるスキルの向上ではない。それは、あなたが書いたコードが、世界中のユーザーにとって「心地よい体験」であると証明するための、エンジニアとしての矜持である。
さあ、今すぐChromeを開き、あなたのアプリケーションの「心臓」がどこで詰まっているのかを確認してほしい。