Google PageSpeed Insights 慘不忍睹的 Next.js 效能優化
最近用Next.js打造了一個功能強大?的網站,但上線後放在 Google PageSpeed Insights 上一測,分數慘不忍睹… 尤其是最大內容繪製 (LCP) 時間竟然高達 15 秒?
UpdatedMarch 24, 2026•2 min read
JJhihHao Wu**近期研究重點包含 AI Agent 的供應鏈攻擊、PII 偵測模型評估,以及醫療 AI 在臨床流程中的安全落地。
在這裡,我分享深度技術實測報告(如 NVIDIA NeMo, WildGuard)與職場技術成長心得,致力於在 AI 浪潮中打造具備資安韌性的解決方案。**Part of seriesAI 工具與模型評測
On this page
Google PageSpeed Insights 慘不忍睹的 Next.js 效能優化緩慢的元兇 —Client-Side Rendering擁抱 (Server-Side Rendering, SSR)Next.js App Router 中的 SSR 實作其他效能優化點
Google PageSpeed Insights 慘不忍睹的 Next.js 效能優化
最近用Next.js打造了一個功能強大?的網站,但上線後放在 Google PageSpeed Insights 上一測,分數慘不忍睹… 尤其是最大內容繪製 (LCP) 時間竟然高達 15 秒?
這一篇將紀錄與分享一次完整的效能優化實戰,紀錄前陣子如何診斷問題與作架構調整,將 LCP 從 15.1 秒的「紅色警戒」成功降至 2 秒內的「綠色健康」範圍。
緩慢的元兇 —Client-Side Rendering
初始的 PageSpeed 報告如下:
Largest Contentful Paint (LCP): 15.1 s
Total Blocking Time (TBT): 1,190 ms
問題還滿明顯的,目前的頁面採用了典型的 CSR 模式:伺服器會先回傳一個基本的 HTML 空殼,頁面上顯示著 “載入中…”,然後所有內容都交給瀏覽器的 JavaScript 來處理。
CSR 的工作流程如下:
瀏覽器下載 HTML。
瀏覽器下載、解析並執行 JavaScript 檔案。
JavaScript 發出 API 請求以獲取資料。
等待資料回傳。
JavaScript 根據資料渲染出完整的資料列表。
這個漫長的鏈條導致了兩個主要問題:
高 LCP:直到第 5 步完成,頁面的「最大內容」(資料列表)才被繪製出來,耗時極長。
高 TBT:在第 2、3、5 步中,瀏覽器主線程被大量 JavaScript 任務阻塞,導致頁面無法即時回應使用者操作。
擁抱 (Server-Side Rendering, SSR)
為了解決這個問題,後來改用 CSR 到 SSR。
SSR 的核心思想是:將原本在客戶端進行的資料獲取和內容渲染工作,轉移到伺服器端完成。
SSR 的工作流程:
瀏覽器發出頁面請求。
Next.js 伺服器接收請求,並在伺服器環境下獲取資料。
伺服器使用獲取到的資料,將頁面完整渲染成 HTML 字串。
伺服器將這個包含所有內容的完整 HTML 回傳給瀏覽器。
瀏覽器接收到 HTML 後,可以直接繪製出完整頁面。
這樣一來,LCP 的時間點被大幅提前到第 5 步,因為瀏覽器不再需要等待 JavaScript 執行和 API 請求。
Next.js App Router 中的 SSR 實作
在 Next.js 的 App Router 架構下,改成SSR也是非常直覺,頁面元件預設就是伺服器元件 (Server Components)。
修改前 (CSR 模式):
// src/app/page.tsx (Before)
'''use client'''; // 必須標記為客戶端元件
import { useState, useEffect } from 'react';
import JobList from '@/components/JobList';
export default function HomePage() {
const [jobs, setJobs] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchJobs() {
const response = await fetch('/api/jobs');
const data = await response.json();
setJobs(data);
setLoading(false);
}
fetchJobs();
}, []);
if (loading) {
return <div>載入中...</div>;
}
return <JobList jobs={jobs} />;
}
修改後 (SSR 模式):
// src/app/page.tsx (After)
import JobList from '@/components/JobList';
import { fetchJobsFromFirestore } from '@/lib/firebase'; // 假設這是從 Firestore 獲取資料的函數
// 頁面變成一個 async function
export default async function HomePage() {
// 1. 直接在伺服器端獲取資料
const initialJobs = await fetchJobsFromFirestore({ limit: 20 });
// 2. 將資料直接傳遞給子元件
return <JobList initialJobs={initialJobs} />;
}
修改之後,先是移除了 '''use client''',讓頁面變回預設的伺服器元件,並將資料獲取的邏輯直接放在 async 的頁面元件中,而Next.js 會在伺服器上 await 這個資料請求完成,然後才把渲染好的 HTML 送出。
其他效能優化點
除了 SSR,這次也一併改了下面幾個小地方:
圖片優化 (
next/image):自動處理圖片格式 (WebP)、尺寸和延遲載入。字體優化 (
next/font):避免字體檔案載入造成的版面位移 (CLS) 和文字閃爍。動態載入 (
next/dynamic):對於不在初始可視區域內的元件(例如頁尾的複雜互動元件),可以使用動態載入,減少主要 JS 檔案的大小。預先連線 (
preconnect):在layout.tsx中使用<link rel="preconnect">提前與重要的第三方網域(如 Google Fonts, Firebase API)建立連線,減少後續請求的延遲。
改善後的頁面再次部署到Cloud Run 後,LCP 和 TBT 兩個部分已經有很大的改善了,應該也跟大部分優化實戰推薦的,從 CSR 轉向 SSR/SSG 是解決現代前端應用 LCP 和 TBT 過高問題最有效的方法之一。
透過利用 Next.js 提供的伺服器元件和資料獲取功能,可以將繁重的工作留在伺服器,換來提供給使用者一個流暢的瀏覽體驗,同時也滿足了搜尋引擎對於網站速度的要求。
其他還是偏慢的部分補功能陸續補足時,順便再花一點時間來修理,下一步應該會丟一個Github Action來在每次部署到Cloud ,利用 PageSpeed Insights API 來測測看有沒有改壞。


Top comments (0)