2026年5月20日
2026年5月20日
WordPressのTotal Blocking Time(TBT)を改善する方法
はじめに
Total Blocking Time(TBT)はメインスレッドを50ms以上ブロックする「長いタスク」の合計時間を測定する指標です。TBTが高いとページの操作応答性が悪化し、INP(Interaction to Next Paint)やTTI(Time to Interactive)も悪化します。PageSpeed InsightsのLab計測に影響し、200ms以下が目標値です。
症状・原因
- PageSpeed InsightsでTBTが赤色(600ms以上)になっている
- ページ読み込み後しばらくボタンをクリックしても反応しない
- JavaScriptの実行時間が長くメインスレッドが占有されている
- サードパーティスクリプト(広告・チャット・分析)がブロッキングしている
解決手順
ステップ1:TBTの原因を特定する
# Chrome DevTools: Performance タブ
# 1. Record ボタンで録画開始
# 2. ページリロード → 5秒後に停止
# 3. Main スレッドで赤いタスク(50ms以上)を確認
# Long Tasks API でプログラム的に検出
// 長いタスクを監視(ブラウザコンソールで実行)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.warn('Long Task:', {
duration: Math.round(entry.duration),
startTime: Math.round(entry.startTime),
attribution: entry.attribution
});
}
});
observer.observe({ entryTypes: ['longtask'] });
# WP-CLIで読み込まれているスクリプトを確認
wp eval "global \$wp_scripts; print_r(array_keys(\$wp_scripts->registered));" 2>/dev/null | head -50
# プラグインごとのスクリプト読み込みを確認
wp eval "
global \$wp_scripts;
foreach(\$wp_scripts->queue as \$handle) {
\$src = \$wp_scripts->registered[\$handle]->src ?? '';
echo \$handle . ': ' . \$src . PHP_EOL;
}
"
ステップ2:JavaScriptを遅延読み込みする
// functions.php: スクリプトに defer/async を追加
add_filter('script_loader_tag', function(string $tag, string $handle, string $src): string {
// 管理画面は除外
if (is_admin()) {
return $tag;
}
// defer を追加するスクリプトのリスト
$defer_scripts = [
'contact-form-7',
'jquery-form',
'wp-embed',
'comment-reply',
];
// async を追加するスクリプトのリスト(順序依存なし)
$async_scripts = [
'google-analytics',
'gtag',
];
if (in_array($handle, $defer_scripts, true)) {
return str_replace(' src=', ' defer src=', $tag);
}
if (in_array($handle, $async_scripts, true)) {
return str_replace(' src=', ' async src=', $tag);
}
return $tag;
}, 10, 3);
// フロントページ以外では jQuery を遅延読み込み
add_action('wp_enqueue_scripts', function(): void {
if (!is_front_page() && !is_home()) {
wp_script_add_data('jquery-core', 'defer', true);
wp_script_add_data('jquery', 'defer', true);
}
});
ステップ3:不要なスクリプトを条件付きで読み込む
// functions.php: 不要なページでスクリプトを無効化
add_action('wp_enqueue_scripts', function(): void {
// 投稿ページ以外ではコメント関連スクリプトを除去
if (!is_singular() || !comments_open()) {
wp_dequeue_script('comment-reply');
}
// ショップページ以外ではWooCommerceスクリプトを除去
if (!is_woocommerce() && !is_cart() && !is_checkout()) {
wp_dequeue_script('woocommerce');
wp_dequeue_script('wc-cart-fragments'); // 特にこれが重い
wp_dequeue_style('woocommerce-general');
}
// Contact Form 7: フォームのあるページだけで読み込む
if (!has_shortcode(get_post()->post_content ?? '', 'contact-form-7')) {
wp_dequeue_script('contact-form-7');
wp_dequeue_style('contact-form-7');
}
}, 99);
ステップ4:サードパーティスクリプトを遅延させる
// テーマの footer や functions.php 経由で出力
// インタラクション発生まで GTM・チャットウィジェットを遅延
(function() {
let loaded = false;
function loadHeavyScripts() {
if (loaded) return;
loaded = true;
// Google Tag Manager を遅延読み込み
(function(w,d,s,l,i){
w[l]=w[l]||[];
w[l].push({'gtm.start': new Date().getTime(), event:'gtm.js'});
const f=d.getElementsByTagName(s)[0],
j=d.createElement(s);
j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i;
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXX');
// チャットウィジェット
window.intercomSettings = { app_id: 'YOUR_APP_ID' };
const s = document.createElement('script');
s.src = 'https://widget.intercom.io/widget/YOUR_APP_ID';
document.head.appendChild(s);
}
// ユーザー操作で読み込む
['scroll', 'mousemove', 'touchstart', 'keydown'].forEach(function(e) {
window.addEventListener(e, loadHeavyScripts, { once: true, passive: true });
});
// 5秒後には必ず読み込む
setTimeout(loadHeavyScripts, 5000);
})();
ステップ5:長い処理をチャンク分割する
// 長いJavaScript処理をyieldで分割(INP改善)
// テーマのmain.jsやカスタムスクリプトに適用
async function processLargeArray(items) {
const CHUNK_SIZE = 100;
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE);
// チャンクを処理
chunk.forEach(item => processItem(item));
// メインスレッドを解放(ブラウザが他の処理できるよう)
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// scheduler.yield() が使える場合(Chrome 115+)
async function processWithYield(items) {
for (const item of items) {
processItem(item);
// 定期的にyield
if ('scheduler' in window && 'yield' in scheduler) {
await scheduler.yield();
}
}
}
注意事項
deferはスクリプトの実行順序を保証しますが、asyncは保証しません。jQuery依存のスクリプトにasyncを付けると動作しなくなります- WooCommerceの
wc-cart-fragmentsスクリプトは毎ページでAJAXリクエストを発生させ重いですが、カート機能に必要です。カートを使わないページのみ除去してください - サードパーティスクリプトの遅延は、GTMイベントのタイミングに影響する場合があります。コンバージョン計測を事前に確認してください
まとめ
TBT改善は①Chrome DevToolsのPerformanceタブで長いタスクを特定、②script_loader_tagフィルターで不要スクリプトにdefer/asyncを付与、③投稿ページ以外でWooCommerce・Contact Form 7スクリプトを除去、④サードパーティスクリプト(GTM・チャット)をユーザー操作まで遅延、⑤長いJS処理をchunkに分割してメインスレッドをyieldする、の順で対応します。