𝟳 𝗛𝗶𝗱𝗱𝗲𝗻 𝗝𝗮𝘃𝗮𝗦𝗰𝗿𝗶𝗽𝘁 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 𝗕𝗼𝘁𝘁𝗹𝗲𝗻𝗲𝗰𝗸𝘀

Webアプリの動作が遅い原因が、アルゴリズムの不備であることは稀です。多くの場合、コードとブラウザのやり取りの仕方に問題があります。

私は300以上のプロダクションアプリをプロファイリングしました。その結果、パフォーマンス問題の73%が以下の7つの原因に起因していることが分かりました。

  1. Layout thrashing(レイアウト・スラッシング) レイアウトのプロパティを読み取った直後にDOMへの書き込みを行うと、これが発生します。これにより、ブラウザはレイアウトの再計算を何度も強制されることになります。 • 影響: レンダリング速度が40〜60%低下。 • 対策: まずすべての読み取りをまとめて行います。その後、requestAnimationFrame を使用してすべての書き込みをまとめて行います。

  2. Unbounded event listeners(解除されないイベントリスナー) リスナーを追加したまま削除しないと、メモリリークの原因になります。これはシングルページアプリケーション(SPA)において大きな問題となります。 • 影響: 1時間ごとに15〜30%のメモリ増加。 • 対策: コンポーネントがアンマウントされる際に、AbortController を使用してリスナーをクリーンアップします。

  3. Synchronous DOM reads in loops(ループ内での同期的なDOM読み取り) DOMへの書き込みも行うループ内で、offsetWidth のようなプロパティを読み取ると、絶え間ないリフロー(reflow)が発生します。 • 影響: 20〜40%のフレームドロップ。 • 対策: ループを開始する前に、レイアウトの値を変数にキャッシュしておきます。

  4. Missing requestAnimationFrame batching(requestAnimationFrame によるバッチ処理の欠如) スクロールやリサイズイベント時に直接DOMを変更すると、実行頻度が高すぎることがあります。 • 影響: スクロール中の10〜25%のジャンク(カクつき)。 • 対策: スクロールハンドラーを requestAnimationFrame でラップし、ペイントサイクルと同期させます。

  5. Large JSON.parse calls(巨大な JSON.parse 呼び出し) 巨大なJSONファイルをパースすると、メインスレッドがブロックされます。これが入力ラグの原因となります。 • 影響: 1回の呼び出しにつき50〜200msのフリーズ。 • 対策: Web Workers を使用して、メインスレッド以外でデータをパースします。

  6. Complex CSS selector matching(複雑なCSSセレクターのマッチング) 深くネストされた、あるいは複雑なセレクターは、スタイルの再計算を遅らせます。 • 影響: スタイル計算時間が5〜15%増加。 • 対策: CSS構造を簡素化し、よりフラットなセレクターを使用します。

  7. Duplicate bundle chunks(重複するバンドルチャンク) 最適化されていないバンドルは、帯域幅を無駄にします。 • 影響: 100〜500KBの転送量の無駄。 • 対策: webpack-bundle-analyzer のようなツールを使用して、重複したコードを見つけて削除します。

進捗を測定する方法: • Chrome DevToolsを開き、Performanceタブに移動します。 • 5秒間のセッションを記録します。 • Mainフレームチャート内の「Long Task」を探します。 • 1つの修正を適用し、Rendering(レンダリング)とPainting(ペイント)の時間を比較します。

Core Web Vitalsのスコアを改善するために、これらの領域に注力してください。

出典: https://dev.to/kui_luo/how-to-find-and-fix-7-hidden-performance-bottlenecks-in-your-javascript-code-ek5