INPの処理時間:原因、最適化、コード例
処理時間が原因のINPの問題を特定し、改善する方法を学びます。
処理時間が引き起こすInteraction to Next Paint (INP)の問題
このページは、Interaction to Next Paint (INP)シリーズの一部です。INPは、ユーザーのインタラクションから次の視覚的な更新までの合計時間を測定します。処理時間はINPを構成する3つのフェーズの2番目であり、入力遅延と表示遅延の間に位置します。INPについて初めて学ぶ場合は、まずINPの問題を特定して修正する方法のガイドを読んでください。
要約:Interaction to Next Paint (INP)は、ユーザーがページを操作してから視覚的な変化を見るまでにかかる時間を測定します。INPは「入力遅延」「処理時間」「表示遅延」の3つの要素に分解できます。
処理時間はINP全体への影響が大きく、遅延の平均約40%を占めます。イベントハンドラの最適化は、処理時間を短縮しINPスコアを改善する最も直接的な方法です。
INPのヒント:レイアウト更新の前に必要な重要なコードを即座に実行し、それ以外のコードをレイアウト更新後にスケジュールすることで、処理時間を最適化できます。
Table of Contents!
処理時間とは?

Interaction to Next Paint (INP)は、「入力遅延」「処理時間」「表示遅延」の3つの部分に分解できます。
処理時間は、ユーザーがウェブページを操作(ボタンのクリックやキーの押下など)した後、ブラウザが関連するすべてのイベントコールバックを実行する時間を指します。処理時間は常に発生します。しかし、イベントコールバックに時間がかかりすぎるとINPの問題を引き起こします。
簡単に言えば、処理時間とはインタラクションに応じて発生する「作業」の時間です。検索ボタンをクリックした場合、クエリの検証、APIリクエストの準備、ローカルステートの更新、分析イベントのトリガーなど、そのクリックイベントに関連するすべてのコードの実行が処理時間に含まれます。イベントハンドラ内のすべてのJavaScriptコードが、処理時間の増加につながります。
処理時間とINP
Interaction to Next Paintの最適化を考えるとき、最初に思い浮かぶのが処理時間かもしれません。これは、ブラウザがレイアウトを更新する前に「完了しなければならない作業」です。
多くの開発者は、コールバック関数の最適化(処理時間の最適化)がINPの改善につながると考えます。それは間違いありません。しかし重要性の観点から見ると、処理時間は最も改善すべき部分というわけではありません。それでも、平均してINP全体の約40%を占めています。

CoreDashでは、1時間に数百万件のCore Web Vitalsデータを収集しています。そのデータによると、処理時間がInteraction to Next Paintの40%を占めています。40%は大きな割合ですが、処理時間を最適化するだけではINPの問題は解決しません。入力遅延(18%)と表示遅延(42%)にも目を向ける必要があります。
処理時間の例:ユーザーがフォーム送信ボタンをクリックした際、フォームデータの検証、サーバーへの送信、レスポンスの処理を行うコードはすべて処理時間に含まれます。これらの処理に時間がかかるほど処理時間は長くなり、結果としてINPスコアが悪化する可能性があります。
処理時間が長くなる原因
処理時間が長くなる一般的な原因は4つあります。不要なコード、最適化されていないコード、コールバックの集中、そしてレイアウトスラッシングです。

- 不要なコード。古くて使われていないコードや、ユーザーのインタラクションに直接関係のないコードは、コールバックの実行時間を長引かせます。これには、次の描画までに完了する必要のない分析用の呼び出し、ロギング、ステートの同期などが含まれます。
- 最適化されていないコード。非効率なコード(通常はループや非効率なDOM検索)は、イベントコールバックの実行速度を不必要に遅くします。よくある例は、ループの前に結果をキャッシュせず、ループ内で
document.querySelectorAll()を使用してDOMを検索することです。 - コールバックの集中。複数のイベントコールバックが近接してスケジュールされると、キューが形成されます。ユーザーのインタラクションによってトリガーされたコールバックがこのキューに滞留すると、応答が遅れているように見えます。たとえば、1回のクリックに対して、
pointerdownハンドラ、mousedownハンドラ、clickハンドラが連続して発火する場合があります。 - レイアウトスラッシング。レイアウトの再計算を引き起こす頻繁なDOM操作は、ブラウザに負荷をかけ、パフォーマンスの低下を招きます。これは、ループ内でレイアウトプロパティの読み取りと書き込みを交互に行い、ブラウザにレイアウトの再計算を何度も強制した場合に発生します。
処理時間の最小化

戦略は2つあります。既存のコードを最適化する(不要なコードを削除し、現在のコードを最適化する)ことと、レイアウト更新の前後どちらで実行すべきコードかを区別することです。レイアウト更新に不可欠なコードを最初に実行し、それ以外のコードはレイアウト更新後に実行します。
- 未使用コードの削除。未使用コードの削除は当たり前に思えるかもしれません。しかし、ほとんどのサイトには、ページやUXに何も貢献せずに実行されるだけの古い未使用コードが存在します。まずすべきことは、不要なコードが実行されていないか確認することです。これは、ツリーシェイキング(tree shaking)、コード分割(code splitting)、Chromeでのコードカバレッジの検査、未使用コードをヒントとして提示する優れたIDEの使用によって実現できます。(ヒント:Tag Managerによって読み込まれるリソースも厳しくチェックしてください。)その他の戦略については、JavaScriptを遅延させる14の方法のガイドを参照してください。
- コールバック実行時間の最小化。JavaScriptプロファイラを使用してコード内のボトルネックを特定し、その部分を最適化してください。冗長な計算を避けるために、メモ化、事前計算、キャッシュなどの手法を検討してください。(ヒント:Chromeのパフォーマンスパネルを使用して、実行時間の長いスクリプトを見つけることができます。)
- 重要なコードの優先とその他のコードのスケジュール。コールバックのコードを最適化したら、すぐに実行する必要があるコードと、遅延できるコードに分割します。実際の例を見てみましょう。

この例では、レイアウト更新の前に実行される(Reactの)コードよりも先に、Google Tag ManagerとFacebookのイベントコールバックが実行されています。解決策は、GTMとFacebookのコールバックをブラウザのアイドル時に実行するようスケジュールすることです。 - レイアウトスラッシングやリフローの回避。レイアウトスラッシングは、ループ内でスタイルの更新と読み取りが混在し、ブラウザがレイアウトを何度も再計算することで発生します。レイアウトスラッシングを避けるには、スタイルの値を取得(get)する前に、すべてのスタイルの変更(set)を実行します。このアプローチによりレイアウト更新の頻度が最小限に抑えられ、ページの高速化につながります。たとえば、各段落の幅をある要素の幅に合わせるループでは、ループの前に一度だけ要素の幅を読み取ります。そして、その値を使用してループ内で段落の幅を更新してください。
setTimeout(0)によるイベントハンドラの分割
イベントハンドラからコードを削除または遅延できない場合の最善策は、ハンドラを小さなチャンクに分割することです。setTimeout(callback, 0)のパターンを使うと、作業を複数のタスクに分割できます。これにより、タスク間にブラウザがレイアウトを更新するタイミングが生まれます。実践的な例を紹介します。
// 変更前:1つの長いイベントハンドラが次の描画をブロックする
button.addEventListener('click', () => {
updateUI(); // 重要:描画前に実行必須
validateForm(); // 重要だが後回し可能
sendAnalytics(); // 重要ではない
syncLocalStorage(); // 重要ではない
});
// 変更後:重要な処理と遅延させる処理に分割
button.addEventListener('click', () => {
updateUI(); // 重要:描画の直前に実行される
setTimeout(() => {
validateForm();
}, 0);
setTimeout(() => {
sendAnalytics();
syncLocalStorage();
}, 0);
}); setTimeout(0)の欠点は、続きの処理がタスクキューの最後に追加されることです。他のタスクがすでにキューに入っている場合、遅延させたコードはすぐには実行されないかもしれません。より予測可能な動作を求める場合は、代わりにscheduler.yield()を使用してください(後述のセクションを参照)。JavaScriptのスクロール処理に特化したパターンについては、専用のガイドを参照してください。
視覚的な更新でのrequestAnimationFrameの使用
イベントハンドラが視覚的な更新をトリガーする必要がある場合、requestAnimationFrame()を使用すると、コードが最適なタイミング(ブラウザが次の再描画を行う直前)で実行されます。これは、レイアウトスラッシングを避けるためにDOMの読み取りと書き込みをバッチ処理する場合に特に有用です。
// requestAnimationFrameを使用して視覚的な更新をバッチ処理する
button.addEventListener('click', () => {
// まずレイアウトプロパティを読み取る(rAFの外部)
const containerWidth = container.offsetWidth;
requestAnimationFrame(() => {
// rAFの内部でレイアウトプロパティを書き込む
items.forEach(item => {
item.style.width = containerWidth + 'px';
});
});
// 視覚的ではない処理をアイドル時間にスケジュールする
requestIdleCallback(() => {
trackButtonClick();
updateSessionState();
});
}); このパターンはDOMの読み取りと書き込みを分離し、強制的な同期レイアウトを防ぎます。視覚的な更新はブラウザのレンダリングパイプラインの理想的なタイミングで実行され、視覚的ではない作業はアイドル時に実行されます。
重要なコードを優先する方法
「重要なコードの優先とその他のコードのスケジュール」というのは抽象的に聞こえるため、実際のコードを見てみましょう。requestIdleCallback()を使用し、main threadにyieldすることで、重要なコードを優先できます。
すぐに実行する必要のない重要度の低いタスクにはrequestIdleCallbackを使用します。GTMイベントをスケジュールする変更前と変更後の例を示します。
/* 変更前:即座にコードを実行する */
gtag('event', '<event_name>', {
'event_category': '<event_category>',
});
/* 変更後:ブラウザのアイドル時に同じコードを実行する */
requestIdleCallback(() => {
gtag('event', '<event_name>', {
'event_category': '<event_category>',
});
}, { timeout: 1000 }); requestIdleCallbackの欠点は、コードが希望通りにすぐ実行されない可能性があることです。その場合は、最も重要なコードが実行された後に「main threadにyield」することで、ブラウザがレイアウトを更新する時間を確保できます。main threadにyieldしてタスクを分割する例を示します。
async function yieldToMain() {
if ('scheduler' in window && 'yield' in window.scheduler) {
return await window.scheduler.yield();
}
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
async function handleClick() {
// ここで最も重要なレイアウト更新を行う
await yieldToMain();
// レイアウト更新後、できるだけ早く実行する必要があるその他のタスクを行う
} scheduler.postTask()によるきめ細かいスケジュール
scheduler.postTask()関数は、優先度を設定することでタスクのきめ細かいスケジュールを可能にします。これによりブラウザが作業の優先順位を判断し、優先度の低いタスクはmain threadにyieldされます。このAPIを本番環境で使用する前に、ブラウザのサポート状況を確認してください。
postTask()関数は3つの優先度設定を受け付けます。最も優先度の低いタスクには"background"、中程度の優先度のタスクには"user-visible"、高い優先度を必要とする重要なタスクには"user-blocking"を指定します。
イベントハンドラ内の各作業に適切な優先度を割り当てることで、必要なタスクをすべて完了しつつ、ブラウザがユーザーのインタラクションにレスポンシブに対応できるようになります。
// 重要なUI処理を高い優先度でスケジュールする
scheduler.postTask(() => {
updateCartBadge();
showConfirmation();
}, { priority: 'user-blocking' });
// データの同期を中程度の優先度でスケジュールする
scheduler.postTask(() => {
syncCartWithServer();
}, { priority: 'user-visible' });
// 分析処理を低い優先度でスケジュールする
scheduler.postTask(() => {
gtag('event', 'add_to_cart', { item: productId });
fbq('track', 'AddToCart');
}, { priority: 'background' }); 実践的な対応策
WordPressおよびReact/Next.jsでの処理時間の最適化へのアプローチを紹介します。
WordPress
WordPressでは、サードパーティのスクリプトに対する制御が限られています。多くのスクリプトはプラグイン経由で追加されます。大抵の場合、これらのスクリプトはページにイベントリスナーを追加し、Interaction to Next Paint (INP)を遅延させるだけで他に何もしません。WordPressサイトで長い処理時間によるINPの問題が発生している場合は、以下の手順を実行してください。
- テーマの設定を確認します。「スムーズスクロール」や「アニメーションメニュー」などの不要なオプションはチェックを外してください。このような設定はINPの問題を引き起こす傾向があります。
- どのスクリプトが処理時間を長くしているか確認します(ヒント:Chromeのパフォーマンスパネルを使用)。もしそれらのスクリプトがプラグイン関連であれば、より少ないJavaScriptでほぼ同じ機能を提供する別のプラグインを探すことを検討してください。
- ページ上でカスタムスクリプトが実行されていることもよくあります。それらのスクリプトを確認し、頻繁にmain threadにyieldしているか、また重要性の低いコールバックを
requestIdleCallback関数でラップしているかを確認してください。 - 未使用のスクリプトをページごとにアンロードします(ヒント:
wp_deregister_scriptを使用)。一部のプラグインは、その機能が不要なページであってもスクリプトを注入する傾向があります。 - Tag Managerを確認し、未使用または不要なタグを削除します。
- 軽量でクリーンなテーマを使用します。「何でもできる」多目的テーマは、スクリプトが多くなり、イベントハンドラが重くなる傾向があります。
- ページビルダーは避けてください。エンドユーザーにページを表示するためにJavaScriptに大きく依存することが多いためです。
React / Next.js
Reactのフック(hooks)とコンカレンシー(並行処理)の機能を利用すると、INPの処理時間を大幅に短縮できます。主なテクニックは以下の通りです。
Reactのコンカレンシー機能を用いたユーザーインタラクションの優先:
React 18ではコンカレンシー機能が導入され、特に入力時のレンダリングが最適化され、よりスムーズなUXが実現しました。
useTransitionとstartTransition:重要ではない更新を後回しのレンダリングとしてマークします。これにより、大きな更新がユーザーのインタラクションをブロックするのを防ぎます。たとえば、検索結果の更新をstartTransitionでラップすれば、検索ボックスへの入力のレスポンスが維持されます。useDeferredValue:UIを不可欠なセクションと重要ではないセクションに分割します。Reactは重要ではない部分のレンダリングを中断し、よりレスポンシブな体験を提供できます。これはフィルタリングされたリストや検索結果のレンダリングに最適です。useOptimistic(React 19以降):ネットワークリクエストなどの非同期操作が進行している間、一時的な楽観的ステートを表示します。これにより、データ取得中もUIのレスポンスが維持されます。
データ取得のためのSuspense(React 18以降)
React 18のSuspenseを使用すると、ブラウザがユーザーインタラクションを優先してレンダリングを最適化できるため、INPの改善に役立ちます。React 16ではコード分割のためにSuspenseが導入されましたが、React 18ではこの機能がデータ取得にまで拡張されています。
- データのロード中は、ローディングインジケーターなどのfallbackコンポーネントが表示されます。
- データが到着すると、Reactはサスペンドされていたコンポーネントのレンダリングを再開します。
- SuspenseとConcurrent Reactの中断可能なレンダリングを組み合わせることで、ユーザーインタラクションが優先されます。ユーザーがサスペンドされたコンポーネントを操作した場合、Reactはそのコンポーネントのレンダリングを優先し、レスポンス性を維持します。
これらの機能により、Reactはバックグラウンドのレンダリング作業よりもユーザーインタラクションを優先できます。
他のINPフェーズについて調べる
処理時間はInteraction to Next Paintの一部にすぎません。INPスコアを完全に最適化するには、他の2つのフェーズにも対処する必要があります。
完全な診断ワークフローについては、INPの問題を特定して修正する方法のガイドを参照してください。また、全体像を把握するにはINPハブページに戻ってください。