重いイベントハンドラー:クリックに500msかかる理由と直し方
何が起きているか
ユーザーがクリックすると、ブラウザは次のフレームを描画する前にイベントハンドラーを最後まで実行します。ハンドラーが数百msの同期処理 — 大きな配列のフィルタ、DOMの構築、JSONの解析 — を行うと、ページはちょうどその間フリーズします。ハンドラーが戻るまでユーザーには何も起きていないように見えます。
これはINPで最も多い問題で、INPの内訳では長いprocessing timeのフェーズとして現れます。
見分け方
- INPの内訳でprocessing timeが支配的(input delayとpresentation delayは小さい)。
- DevTools Performanceで、イベント直後に1つの長いタスクが始まり、あなたのハンドラー関数に帰属される。
- 遅さがデータ量に比例する:行や項目が多いほどクリックが重く感じる。
直し方
- まずUIを更新し(押下状態、スピナー)、重い処理の前にscheduler.yield()(フォールバックはsetTimeout(0))でブラウザに制御を渡す。
- 大きなループをチャンクに分け、チャンクの間で制御を渡してブラウザが描画できるようにする。
- 純粋な計算(解析、フィルタ、差分)をWeb Workerに移す — メインスレッドは入力を送り結果を受け取るだけ。
問題
button.addEventListener("click", () => {
const results = filterRows(allRows) // 400 ms of sync work
renderTable(results) // user saw nothing until now
})
修正
button.addEventListener("click", async () => {
button.classList.add("loading") // instant feedback, painted first
await scheduler.yield() // let the browser paint
const results = filterRows(allRows)
renderTable(results)
button.classList.remove("loading")
})
サイトを無料でスキャン →