Next.js 效能分析與除錯技巧

在現代 Web 開發中,效能就是使用者體驗。Next.js 雖然預設做了很多優化(如 Image Optimization, Automatic Static Optimization),但隨著專案增長,我們仍需主動監控與調校。

本篇將介紹從「編譯分析」到「執行期監控」的完整除錯與優化流程。

分析 Bundle Size (@next/bundle-analyzer)

JavaScript 的體積直接影響網頁的載入速度 (TBT, TTI)。當你發現頁面載入變慢,第一步是檢查「我們到底打包了什麼?」。

安裝與設定

npm install -D @next/bundle-analyzer cross-env

修改 next.config.js

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({
  // 其他 Next.js 設定
});

執行分析

package.json 加入指令:

"scripts": {
  "analyze": "cross-env ANALYZE=true next build"
}

執行 npm run analyze 後,瀏覽器會自動開啟兩個互動式圖表:

  1. Server Bundle:伺服器端執行的程式碼。
  2. Client Bundle:瀏覽器下載的程式碼(重點關注)。

優化策略

  • 檢查是否有巨大的第三方套件(如 lodash, moment.js)。
  • 將大型套件改為 Lazy Loading (next/dynamic)。

核心網站指標 (Core Web Vitals)

Google 定義的三大關鍵指標,直接影響 SEO 排名。

  • LCP (Largest Contentful Paint):視窗內最大內容(圖片或文字)的顯示時間。目標 < 2.5s。
  • INP (Interaction to Next Paint):點擊後的反應延遲。目標 < 200ms。
  • CLS (Cumulative Layout Shift):版面位移穩定度。目標 < 0.1。

實時監控 (Real-time Reporting)

Next.js 內建 useReportWebVitals hook,可以將數據發送到你的分析後台(如 GA4)。

// app/_components/WebVitals.tsx
'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitals() {
  useReportWebVitals((metric) => {
    // 範例:發送事件到 Google Analytics
    if (window.gtag) {
      window.gtag('event', metric.name, {
        value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
        event_label: metric.id,
        non_interaction: true,
      });
    }
    console.log(metric); // 開發時可以直接 log
  });

  return null;
}

Chrome DevTools 深度除錯

找出 React Render 瓶頸

安裝 React Developer Tools 擴充功能,使用 "Profiler" 頁籤。

  • 勾選 "Record why each component rendered while profiling"。
  • 錄製一段操作,觀察哪些組件發生了不必要的 Re-render。

模擬低速網路 (Network Throttling)

Network 頁籤中,將網路設為 "Fast 3G" 或 "Slow 3G"。這對於測試 loading.tsx (Streaming) 的骨架屏效果非常有效,能確保使用者在弱網下仍有好的感知體驗。

解決 Hydration Error

這是 Next.js 開發者最頭痛的錯誤:Text content does not match server-rendered HTML

發生原因

伺服器端生成的 HTML (SSR) 與瀏覽器端 React 首次執行的結果 (CSR) 不一致。

  1. 時間戳記new Date() 在 Server 與 Client 時間不同。
  2. HTML 結構非法:例如 <p> 裡面包了 <div>
  3. 瀏覽器專屬 API:使用了 windowlocalStorage 影響渲染結果。

標準解法

使用 useEffect 確保動態內容只在 Client 端渲染:

'use client';
import { useState, useEffect } from 'react';

export default function CurrentTime() {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return null; // 或是回傳一個與 Server 一致的 Loading 狀態
  }

  return <div>{new Date().toLocaleTimeString()}</div>;
}

或者使用 suppressHydrationWarning(僅限必要時):

<time suppressHydrationWarning>{new Date().toLocaleTimeString()}</time>

小結

  • 定期健檢:每次發布大版本前,執行 npm run analyze 檢查 Bundle Size。
  • 關注 LCP:優化圖片尺寸 (next/image) 與字型載入 (next/font)。
  • 消滅 Hydration Error:保持 SSR 與 CSR 的初始狀態一致性,這對 SEO 與使用者體驗都至關重要。

優化是一場馬拉松,善用工具能讓你事半功倍。