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 後,瀏覽器會自動開啟兩個互動式圖表:
- Server Bundle:伺服器端執行的程式碼。
- 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) 不一致。
- 時間戳記:
new Date()在 Server 與 Client 時間不同。 - HTML 結構非法:例如
<p>裡面包了<div>。 - 瀏覽器專屬 API:使用了
window或localStorage影響渲染結果。
標準解法
使用 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 與使用者體驗都至關重要。
優化是一場馬拉松,善用工具能讓你事半功倍。