ブラウザの「悲鳴」を可視化せよ:PerformanceObserverで実現する現場のパフォーマンス観測
やあ。フロントエンドの現場で、Lighthouseのスコアを眺めては「なぜかスコアが安定しない」と頭を抱えた経験はないかな?
「コードは綺麗なのに、なぜかUXが重い」「特定のユーザー環境でだけカクつく」。そんな時、多くのエンジニアは勘と経験で怪しい関数を疑い始める。だが、現代のフロントエンド開発において、「ブラウザの心臓部で何が起きているか」を推測で語るのは、もう時代遅れだ。
今日は、ブラウザのレンダリングパイプラインの裏側を覗き見し、`PerformanceObserver` APIを使って、ブラウザが発する「悲鳴」を正確にキャッチする方法を伝授しよう。
—
なぜ「計測」にPerformanceObserverを使うのか?
従来の `window.performance.getEntries()` を使ったポーリング計測は、いわば「終わった後にゴミ箱を漁る」ようなものだ。しかし、`PerformanceObserver` は違う。これはブラウザのメインスレッドで発生するイベントをフックし、非同期に通知を受け取る「観測所」だ。
ブラウザがHTMLをパースし、DOM/CSSOMを構築し、レンダリングツリーを経てピクセルを画面に描画する――この激流のようなプロセスの中で、どこがボトルネックになっているかを「リアルタイム」に観測できる。
現場で即戦力になる実装例
まずは、多くの現場で「UXの敵」となる Long Task(メインスレッドを長時間占有する処理) と Layout Shift(レイアウトのズレ) を監視するためのコードを見てほしい。これをそのままユーティリティとしてプロジェクトに仕込んでみてくれ。
/
- ブラウザのパフォーマンスを監視するオブザーバーの初期化
/
function setupPerformanceMonitoring() {
// 1. Long Taskの監視
// 50ms以上のメインスレッド占有を「ロングタスク」として検知する
const longTaskObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.warn(`[パフォーマンス警告]: ロングタスクを検知しました!`, {
duration: `${entry.duration.toFixed(2)}ms`,
startTime: entry.startTime
});
// ここで監視ツール(SentryやDatadog等)へ送信するのが現場の流儀だ
});
});
longTaskObserver.observe({ entryTypes: [‘longtask’] });
// 2. Layout Shift (CLS) の監視
// コンテンツがガタつく瞬間を捉える
const layoutShiftObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// ユーザー入力直後のズレを除外するのが正確な計測のコツ
if (!entry.hadRecentInput) {
console.info(`[レイアウトシフト]: 視覚的なズレが発生しました`, {
value: entry.value,
sources: entry.sources
});
}
}
});
layoutShiftObserver.observe({ type: ‘layout-shift’, buffered: true });
}
// アプリ起動時に実行
setupPerformanceMonitoring();
—
ブラウザの裏側で起きていること:なぜ「観測」が必要か
このコードを動かすと、ブラウザが裏側でどれほど「ギリギリの綱渡り」をしているかが見えてくるはずだ。
- DOM/CSSOM構築のコスト: ブラウザはHTMLをパースしながらDOMを組み、CSSからCSSOMを生成する。この2つが合わさって初めて「レンダリングツリー」ができる。もしJavaScriptが同期的に重い処理を行えば、この構築プロセスは即座に停止する。`longtask` を監視するということは、「レンダリングを止めている犯人」を特定するということだ。
- レイアウトシフトの正体: `layout-shift` は、DOMの変更や、遅れて読み込まれた画像が原因でレンダリングツリーが再計算された結果だ。ブラウザにとって、一度確定したレイアウトを再計算するのは非常に重いコストがかかる。ユーザーの指先を空振りさせるようなUIの動きは、このイベントを追うことでしか突き止められない。
シニアからのアドバイス:計測の「落とし穴」
実装にあたって、一つだけ忠告がある。「計測のためのコードがパフォーマンスを悪化させる」という皮肉な事態だけは避けろ。
1. Bufferedオプションの活用: `buffered: true` を指定することで、オブザーバーが登録される前の過去のイベントも取得できる。初期化のタイミングに左右されない堅牢な計測が可能になる。
2. サンプリングと送信の抑制: ユーザーの端末で頻繁にイベントが発生した場合、すべてをサーバーに投げるとネットワーク帯域を圧迫する。閾値を設けて、重大なパフォーマンス劣化のみを報告するように工夫すること。
3. 開発環境と本番環境の使い分け: `console.warn` で終わらせず、本番ではプロダクトの監視ツールに飛ばす。そして、「どのページで、どのコンポーネントが」原因かまでドリルダウンできるようにしておくのが、一人前のエンジニアの作法だ。
最後に
ブラウザは単なるWebページの表示器ではない。緻密な計算と最適化が繰り返される、高度なOSのようなものだ。その挙動を深く理解し、`PerformanceObserver` で可視化することは、「動くものを作る」段階から「品質を制御する」段階への脱皮を意味する。
さあ、エディタを開いて、君が担当しているアプリケーションの「影の挙動」を暴いてみてくれ。現場からは以上だ。また何かあればいつでも聞きに来い。

コメント