ページ表示が速い!遅い!その原因、Total Blocking Time (TBT) をこっそり覗いてみよう!
Webサイトを見ているとき、「あれ?なんか重いな…」と感じたこと、ありませんか?せっかく素敵なデザインや役立つ情報があっても、表示が遅いと「もういいや…」って、ついつい他のサイトに移ってしまうんですよね。
実は、その「重さ」や「遅さ」を数値で表す指標の一つに、Total Blocking Time (TBT) というものがあります。ちょっと専門的に聞こえるかもしれませんが、大丈夫!今回は、このTBTが一体何者なのか、そしてどうすれば私たちのサイトを「速い!」って思ってもらえるように改善できるのか、身近な例え話を交えながら、優しく紐解いていきましょう。
このTBTを理解して、あなたのサイトをもっと魅力的に、もっと速くするお手伝いができれば嬉しいです!
そもそも「レンダリング」って何?ブラウザ君の仕事ぶりを覗いてみよう!
TBTの話に入る前に、まずはブラウザがどうやってWebページを表示しているのか、その裏側をちょっとだけ覗いてみましょう。
Webページって、実はたくさんの「指示書」の集まりなんです。HTMLで「ここに画像を出してね」「この文字は大きくしてね」って指示し、CSSで「この画像は右寄せにしてね」「この文字は赤色にしてね」って指示し、JavaScriptで「ボタンが押されたら、この文字を書き換えてね」なんて、動的な指示も出します。
ブラウザ君は、これらの指示書を一生懸命読み込んで、私たちが見ている画面に絵を描いていくんです。この「絵を描く」作業のことを、専門用語で レンダリング と言います。
レンダリングは、大きく分けていくつかのステップに分かれます。
- HTMLの解析とDOMツリーの構築: まず、HTMLの指示書を読んで、ページの骨組み(DOMツリー)を作ります。これは、まるで建物の設計図を読み解くようなイメージですね。
- CSSの解析とCSSOMツリーの構築: 次に、CSSの指示書を読んで、見た目の装飾情報(CSSOMツリー)を作ります。設計図に色を塗ったり、家具の配置を決めたりする感じです。
- Render Treeの構築: DOMツリーとCSSOMツリーを組み合わせて、実際に画面に描画する要素(Render Tree)を特定します。見えない要素(`display: none;` とか)は、ここで省かれます。
- レイアウト(リフロー): 画面上のどこに、どれくらいの大きさで要素を配置するかを計算します。これが リフロー という作業で、いわば「家具をどこに置くか」を決める作業です。要素のサイズや位置が変わると、その影響で他の要素も再計算する必要が出てくるので、結構大変な作業なんです。
- ペイント(リペイント): 計算されたレイアウトに基づいて、実際に画面に色を塗ったり、文字を描いたりします。これが リペイント です。家具の配置が決まったら、その周りの壁に色を塗ったり、装飾を施したりするイメージですね。
- コンポジット(合成): 複数のレイヤーに分かれた描画結果を、最終的に一枚の画像として画面に合成します。これは、描画済みのパーツをパズルのように組み合わせて、完成した絵にする作業です。
「メインスレッド」って何?ブラウザ君の「お一人様作業」
さて、ここからがTBTの核心に迫るお話です。ブラウザ君のレンダリング作業、特にJavaScriptの実行や、先ほど説明したリフロー、リペイントといった、ユーザーの操作に直接関わるような重要な処理は、メインスレッド と呼ばれる一本の道で、順番に実行されています。
例えるなら、メインスレッドは、あなたがお店で一人でレジに並び、商品を自分で選んで、自分で袋詰めまで全部やるようなイメージです。他に人が並んでいても、あなたが作業を終えるまで、後ろの人は待たなければなりません。
このメインスレッドが、他の作業(例えば、後から読み込まれるJavaScriptが実行されたり、複雑なCSSの計算をしたり)で長時間「塞がれてしまう」と、ユーザーがボタンをクリックしても反応しなかったり、スクロールがカクカクしたり…といった、いわゆる「重い」「遅い」と感じる原因になってしまうんです。
Total Blocking Time (TBT) とは?「待ち時間」を合計したもの
ここでようやく Total Blocking Time (TBT) の登場です!
TBTは、Webサイトが表示されてから、ユーザーが実際に操作できるようになるまでの間(Time to Interactive: TTI と呼ばれます)に、メインスレッドが「ブロック」(塞がれて)されていた時間の合計を表す指標です。
もう少し具体的に言うと、
1. First Contentful Paint (FCP): ページに最初のコンテンツ(文字や画像)が表示された瞬間。
2. Time to Interactive (TTI): ページが完全にインタラクティブになり、ユーザーの操作にスムーズに応答できるようになるまでの時間。
この FCPからTTIまでの間に、メインスレッドが50ミリ秒(0.05秒)以上ブロックされていたタスク(作業)があった場合、そのブロックされていた時間のうち、50ミリ秒を超えた分だけをTBTとしてカウントする んです。
たとえば、
- あるJavaScriptの処理が、メインスレッドを100ミリ秒ブロックしたとします。この場合、50ミリ秒を超える 50ミリ秒 がTBTに加算されます。
- 別の処理が、メインスレッドを70ミリ秒ブロックしたとします。この場合も、50ミリ秒を超える 20ミリ秒 がTBTに加算されます。
これらのブロック時間の合計が、そのページのTBTとなるわけです。
TBTが高いということは、FCPで何か表示されても、ユーザーが実際にページを操作できるようになるまでに、ブラウザ君が「ちょっと待って!今、他の大事な作業で手が離せないんだ!」と、何度もユーザーを待たせてしまっている状態と言えます。
なぜTBTが重要なのか?ユーザー体験との深い繋がり
TBTが高いと、ユーザーは以下のような不快な体験をしてしまう可能性があります。
- クリックしても反応がない: ボタンを押しても、何も起こらない時間が長くなる。
- スクロールがカクカクする: ページをスクロールしても、滑らかに動かず、途切れるような動きになる。
- 入力が遅れる: テキストフィールドに入力しても、文字が表示されるまでに時間がかかる。
これらの体験は、ユーザーに「このサイトは使いにくいな」「応答が悪いな」という印象を与え、すぐに離脱されてしまう原因になりかねません。特に、JavaScriptに依存するインタラクティブな要素が多いサイトでは、TBTの最適化は非常に重要になります。
TBTを減らすための具体的な戦略:身近な例えで理解しよう!
さて、TBTが高いのは困る!じゃあ、どうすればTBTを減らせるのでしょうか?ここでは、いくつか具体的な戦略を、身近な例え話を交えながらご紹介します。
1. JavaScriptの「重い処理」を小さく分割する(お買い物リストを小分けにする)
一番のTBTの敵は、長くて重いJavaScriptの処理です。まるで、お買い物リストに「全部の食材を一度に買ってきて、一度に全部調理する」と書いてあるようなもの。これを、
- 「まず野菜を買ってきて、調理する」
- 「次に肉を買ってきて、調理する」
- 「最後にデザートを買ってきて、調理する」
のように、小さく分割して、一つずつ完了させていくイメージです。
JavaScriptでは、`setTimeout` や `requestIdleCallback` といった仕組みを使って、重い処理を小さな塊に分割し、メインスレッドが少し空いたタイミングで実行させることができます。
【サンプルコード例:`setTimeout` を使った処理の分割】
// 実行したい重い処理の関数
function heavyTask() {
console.log(“重い処理を開始します…”);
// ここに時間がかかる処理が入ります
// 例:大量のデータを処理する、複雑な計算をするなど
let sum = 0;
for (let i = 0; i < 100000000; i++) { // 例として大きなループ
sum += i;
}
console.log("重い処理が完了しました。結果:", sum);
}
// ページ読み込み時にすぐに実行せず、少し待ってから実行する
// setTimeout(heavyTask, 0); // 0ミリ秒後、という指定でも、一度イベントループを回してくれる
// もっと効果的なのは、イベントループが空いたタイミングを狙うこと
// 例えば、ボタンクリックなどのユーザー操作をトリガーにするのが一般的です
// 【最適化の例】
// ユーザーがボタンを押したら、重い処理を実行する
document.getElementById('myButton').addEventListener('click', function() {
console.log("ボタンがクリックされました!");
// 重い処理を分割して実行する(例:50ミリ秒ごとに実行)
let chunkIndex = 0;
const chunkSize = 10000000; // 処理の塊のサイズを調整
const totalItems = 100000000; // 全体の処理数
function processChunk() {
const startTime = performance.now(); // 処理開始時間
console.log(`チャンク ${chunkIndex} を処理中...`);
const start = chunkIndex chunkSize;
const end = Math.min(start + chunkSize, totalItems);
let currentSum = 0;
for (let i = start; i < end; i++) {
currentSum += i;
}
console.log(`チャンク ${chunkIndex} 完了。部分合計: ${currentSum}`);
chunkIndex++; // 次のチャンクへ
const endTime = performance.now(); // 処理終了時間
const duration = endTime - startTime; // 処理時間
// もし50ミリ秒以上かかっていたら、次の処理をすぐに実行しない
if (duration > 50) {
console.warn(`チャンク ${chunkIndex – 1} の処理に ${duration.toFixed(2)} ミリ秒かかりました。次の処理を遅延させます。`);
// setTimeoutで次の処理を遅延させる(メインスレッドを解放)
setTimeout(processChunk, 0); // 0ミリ秒でも、イベントループを回す
} else {
// 50ミリ秒未満なら、できるだけ早く次の処理を実行
if (chunkIndex chunkSize < totalItems) {
processChunk(); // 再帰的に呼び出す
} else {
console.log("全ての処理が完了しました!");
}
}
}
// 最初のチャンク処理を開始
processChunk();
});
このコードは、本来なら一度に実行される重い計算処理を、小さな「チャンク」に分割し、それぞれのチャンクの処理時間が50ミリ秒を超えた場合に、`setTimeout` を使って意図的に遅延させています。これにより、メインスレッドが長時間ブロックされるのを防ぎ、ユーザーが他の操作をしやすくなります。
2. 使っていないJavaScriptコードを削除する(不要なものをカゴから出す)
お買い物で、本当は必要ないのにカゴに入れてしまうものってありますよね。Webサイトでも、使われていないJavaScriptコードが読み込まれていると、それだけでブラウザ君の負担になります。
- 不要なライブラリやプラグインを削除する: 本当にその機能が必要か?代替手段はないか?を検討しましょう。
- コードの最適化: 使われていない関数や変数がないか、コードを見直しましょう。
- コード分割(Code Splitting): ページごとに必要なJavaScriptだけを読み込むようにします。これは、まるで「お肉売り場に行くときは肉だけ、野菜売り場に行くときは野菜だけ」のように、必要なものだけを持ってくるイメージです。
3. CSSの計算を減らす(配置や装飾の指示をシンプルに)
CSSの複雑な指定も、ブラウザ君の計算負荷を増やします。特に、レイアウト(リフロー)に影響を与えるような変更は、計算コストが高い傾向があります。
- `box-shadow` や `filter` などの複雑なプロパティの使用を控える: これらは描画負荷が高いことがあります。
- アニメーションの最適化: CSSアニメーションで、レイアウトに影響を与えない `transform` や `opacity` を使うようにすると、パフォーマンスが向上します。これは、家具の配置を変える(リフロー)のではなく、家具の色を変えたり、光沢を調整したりする(ペイントやコンポジットの一部)ようなイメージで、比較的軽い処理です。
4. ユーザー操作を待ってから実行する(「〇〇しますか?」と確認する)
ユーザーがページを訪れた瞬間から、すべてのJavaScriptを実行する必要はありません。ユーザーが実際にその機能を使おうとしたときに、初めて実行するように遅延させることも有効です。
- 遅延読み込み(Lazy Loading): 画像や動画、コメント欄などを、ユーザーがスクロールして画面に表示される直前に読み込むようにします。
- イベントリスナーの最適化: ユーザーの操作(クリック、スクロールなど)を検知するイベントリスナーを、必要最低限に絞りましょう。
TBTを計測するには?開発者ツールを活用しよう!
「うちのサイトのTBTはどれくらいなんだろう?」と気になったら、ブラウザの開発者ツールを使ってみましょう。
多くのブラウザ(Chrome、Firefoxなど)には、パフォーマンスを計測する機能が搭載されています。
1. Webページを開き、右クリックして「検証」または「要素を調査」を選択します。
2. 「Performance」タブ(またはそれに類するタブ)を開きます。
3. 記録ボタン(●)を押してページをリロードするか、操作を行います。
4. 記録が終了したら、パフォーマンスのグラフを確認します。
5. グラフの中から、「Main」スレッドの活動を確認します。ここで、長時間ブロックされているタスク(赤色のバーなど)を見つけ、その時間を合計することで、おおよそのTBTを把握できます。
最近では、Lighthouseなどのツールを使えば、TBTだけでなく、First Contentful Paint (FCP) や Time to Interactive (TTI) といった他の重要な指標もまとめて計測してくれるので、ぜひ活用してみてください。
まとめ:ユーザー体験を最優先に、ブラウザ君に優しく!
Total Blocking Time (TBT) は、Webサイトの応答性やユーザー体験を左右する重要な指標です。JavaScriptの実行、リフロー、リペイントといったメインスレッドのブロック時間を合計したもので、この値が高いとユーザーは「遅い」「使いにくい」と感じてしまいます。
TBTを削減するためには、
- 重いJavaScript処理を小さく分割する
- 不要なコードを削除する
- CSSの計算負荷を減らす
- ユーザー操作を待ってから実行する
といった戦略が有効です。
これらの最適化は、単に数値を改善するためだけでなく、何よりもユーザーが快適にWebサイトを利用できるようにするための大切な取り組みです。ブラウザ君の仕事ぶりを理解し、彼に優しく接することで、きっとあなたのWebサイトは、より多くの人にとって心地よい場所になるはずです。
ぜひ、今日からあなたのサイトのTBT改善にチャレンジしてみてくださいね!応援しています!

コメント