Next.js 性能優化:2026 完整指南
我花了過去四年時間為客戶優化 Next.js 應用程式,從年營收 5000 萬美元的電商商店到擁有 10 萬多日活躍用戶的 SaaS 儀表板。我學到的一些東西與文檔完全吻合。但大部分情況並非如此。這是我希望在開始時就有人交給我的指南 -- 已針對 Next.js 15 和 2026 年實際重要的模式進行了更新。
性能不是你在最後才加上的功能。它是你從第一天開始做出的一系列決策,每個決策都會產生複合效應。錯過幾個早期決策,你就在看著需要重寫。把它們做對,你的應用程式就像運行在用戶本地機器上一樣。
讓我們開始吧。
目錄
- 理解 Next.js 15 性能基礎知識
- 衡量真正重要的指標
- 服務器組件:你可能未充分利用的最大勝利
- 包大小優化
- 圖像和媒體優化
- 數據獲取和緩存策略
- 邊緣運行時和中間件性能
- 數據庫和 API 層優化
- 渲染策略選擇
- 第三方腳本管理
- 基礎設施和部署
- 真實世界基準和案例研究
- 常見問題

理解 Next.js 15 性能基礎知識
Next.js 15(自 2025 年末穩定)在底層性能工作原理方面帶來了一些重大變化。Turbopack 打包器現在是開發和生產構建的默認選項。App Router 已完全成熟。而令 Next.js 14 中基本所有人都困惑的緩存行為已經得到理性化處理。
你需要內化的是:Next.js 為你提供了多種渲染策略,為給定頁面選擇錯誤的策略是我看到的最常見的性能錯誤。靜態生成、服務器端渲染、增量靜態再生成、部分預渲染、流式傳輸 -- 每種都有特定的用例。對於每週變化一次的營銷頁面使用 SSR 只是在徒勞地浪費計算資源。
性能心智模型
將 Next.js 性能視為三層:
- 構建時決策 -- 什麼被預渲染,什麼是動態的,代碼如何分割
- 服務器時執行 -- 你的服務器響應速度有多快,緩存,邊緣與原點
- 客戶端體驗 -- 包大小,水合成本,交互就緒性
每層都會乘以其他層。快速的服務器響應在你向中端安卓手機發送 500KB 的 JavaScript 需要 3 秒才能水合時毫無意義。
衡量真正重要的指標
在優化任何東西之前,你需要衡量。而且你需要衡量正確的事情。
核心網頁指標在 2026 年仍然是谷歌的排名信號,但閾值已收緊。以下是現狀:
| 指標 | 良好 | 需要改進 | 較差 |
|---|---|---|---|
| LCP(最大內容繪製) | ≤ 2.0s | 2.0s – 3.5s | > 3.5s |
| INP(交互到下一次繪製) | ≤ 150ms | 150ms – 300ms | > 300ms |
| CLS(累計佈局偏移) | ≤ 0.1 | 0.1 – 0.25 | > 0.25 |
| TTFB(首字節時間) | ≤ 400ms | 400ms – 800ms | > 800ms |
我實際使用的工具
- Vercel Speed Insights -- 如果你在 Vercel 上,這是顯而易見的。真實用戶數據,而非合成數據。
next/bundle-analyzer-- 每週運行一次。當你不注意時,包大小會悄悄增加。- Chrome DevTools Performance 標籤 -- 仍然是調試水合問題的金標準。
- WebPageTest -- 用於在真實位置的真實設備上進行測試。膠片視圖無價。
- Sentry Performance Monitoring -- 用於在生產環境中跟踪真實的 API 響應時間和服務器組件渲染持續時間。
# 將包分析器添加到你的項目
npm install @next/bundle-analyzer
// next.config.mjs
import withBundleAnalyzer from '@next/bundle-analyzer';
const config = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})({
// your config here
});
export default config;
運行 ANALYZE=true npm run build 並實際查看輸出。我保證你會發現至少一個比你預期大得多的庫。
服務器組件:你可能未充分利用的最大勝利
服務器組件是現代 Next.js 中最大的性能改進。它們向客戶端發送零 JavaScript。零。HTML 在服務器上渲染,流式傳輸到瀏覽器,組件永遠不會水合。
但人們在這裡出錯的地方:他們過早添加 'use client'。我審查過的代碼庫中,80% 的組件是客戶端組件,因為開發人員習慣了舊的 Pages Router 心智模型。每個 'use client' 指令都是一個水合邊界。每個水合邊界都是瀏覽器必須下載、解析和執行的 JavaScript。
我遵循的規則
默認將組件保持為服務器組件。只有在絕對必要時才添加 'use client':
- 事件處理程序(onClick、onChange 等)
- useState、useEffect、useRef
- 僅限瀏覽器的 API(localStorage、window 等)
- 使用 hooks 的第三方客戶端庫
組合模式
當你需要在較大組件的一小部分中進行交互時,不要將整個東西都變成客戶端組件。反而:
// app/product/[id]/page.tsx (服務器組件)
import { getProduct } from '@/lib/products';
import { AddToCartButton } from '@/components/AddToCartButton';
import { ProductReviews } from '@/components/ProductReviews';
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
{/* 只有這個小按鈕是客戶端組件 */}
<AddToCartButton productId={product.id} price={product.price} />
{/* 整個評論部分保持在服務器上 */}
<ProductReviews productId={product.id} />
</div>
);
}
// components/AddToCartButton.tsx
'use client';
export function AddToCartButton({ productId, price }: { productId: string; price: number }) {
const handleClick = () => {
// cart logic
};
return <button onClick={handleClick}>Add to Cart -- ${price}</button>;
}
這個模式單獨就在我們合作過的項目中減少了 40-60% 的包大小,通過我們的 Next.js 開發實踐。

包大小優化
Next.js 15 中的 Turbopack 比 webpack 更好地處理樹搖,但它無法拯救你免於糟糕的導入。
命名導入很重要
// 不好 -- 導入整個庫
import _ from 'lodash';
const sorted = _.sortBy(items, 'name');
// 好 -- 只導入你需要的
import sortBy from 'lodash/sortBy';
const sorted = sortBy(items, 'name');
// 最好 -- 你甚至需要 lodash 嗎?
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name));
2026 年常見的包膨脹程序
| 庫 | 典型大小(gzip) | 替代品 | 大小節省 |
|---|---|---|---|
| moment.js | 72KB | date-fns(可樹搖) | ~60KB |
| lodash(完整) | 71KB | 原生 JS / lodash-es | ~65KB |
| chart.js | 65KB | lightweight-charts | ~45KB |
| react-icons(全部) | 40KB+ | 單個圖標包 | ~35KB |
| framer-motion | 44KB | motion(精簡)或 CSS | ~30KB |
重組件的動態導入
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('@/components/Chart'), {
loading: () => <div className="h-64 animate-pulse bg-gray-100 rounded" />,
ssr: false, // 如果只有瀏覽器才支持,不在服務器上渲染
});
我對任何超過 20KB 且不在首屏的東西使用動態導入。圖表、富文本編輯器、地圖、複雜模態框 -- 都是延遲加載的。
圖像和媒體優化
next/image 組件在 Next.js 15 中有了顯著改進。它現在默認支持 AVIF(以及 WebP),自動大小檢測更可靠。
關鍵圖像優化
import Image from 'next/image';
// 英雄圖像 -- 首屏上方,需要優先級
<Image
src="/hero.jpg"
alt="Product showcase"
width={1200}
height={630}
priority // 預加載此圖像
sizes="100vw"
quality={80} // 80 通常是最佳點
/>
// 首屏下方圖像 -- 默認延遲加載
<Image
src="/feature.jpg"
alt="Feature detail"
width={600}
height={400}
sizes="(max-width: 768px) 100vw, 50vw"
placeholder="blur"
blurDataURL={feature.blurHash}
/>
`sizes` 屬性不是可選的
我經常看到這被跳過。沒有正確的 sizes 屬性,瀏覽器會下載最大的圖像變體,無論視口如何。在移動設備上,這可能是為 375px 屏幕加載 2400px 寬的圖像。指定你的 sizes。每次都要。
視頻優化
對於視頻,不要使用帶有巨大 MP4 的 <video> 標籤。在 2026 年,舉動是:
- 使用 FFmpeg 或 Mux 之類的服務轉碼為多個質量
- 對任何超過 10 秒的視頻使用 HLS 流式傳輸
- 對於短動畫,考慮 WebM 甚至動畫 AVIF
- 使用
IntersectionObserver延遲加載首屏下方的視頻
數據獲取和緩存策略
Next.js 15 簡化了與 14 中令人困惑的默認值相比的緩存。關鍵變化:默認情況下不再緩存任何東西。你明確選擇進入緩存。這理智得多。
使用 `use cache` 指令進行緩存
Next.js 15 引入了 use cache 指令(目前在 canary 版本,預計在 15.2 穩定):
async function getProducts() {
'use cache';
const products = await db.products.findMany();
return products;
}
對於 fetch API,緩存由明確控制:
// 沒有緩存(Next.js 15 中的默認值)
const data = await fetch('https://api.example.com/data');
// 緩存直到手動重新驗證
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache',
});
// 每 60 秒重新驗證
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 },
});
按內容類型緩存策略
| 內容類型 | 策略 | 重新驗證 | 示例 |
|---|---|---|---|
| 營銷頁面 | 靜態(構建時) | 部署時 | 主頁、關於頁面 |
| 產品列表 | ISR | 60-300 秒 | 類別頁面 |
| 用戶儀表板 | 動態(不緩存) | 每次請求 | 帳戶設置 |
| 博客文章 | ISR | 3600 秒 | CMS 驅動的內容 |
| 搜索結果 | 動態 + 客戶端緩存 | SWR 模式 | 搜索頁面 |
| API 數據 | 服務器 + CDN 緩存 | 因情況而異 | REST/GraphQL |
對於使用無頭 CMS 的項目(大多數我們在 無頭 CMS 開發實踐中構建的項目),ISR 配合 webhook 觸發的重新驗證是黃金標準。內容更新在幾秒內出現,無需重建整個網站。
邊緣運行時和中間件性能
邊緣運行時在靠近用戶的 CDN 節點上運行你的代碼。TTFB 急劇下降 -- 我們測量過邊緣的 50-150ms TTFB 與單區域原點的 300-800ms。
但有一個陷阱:邊緣運行時不支持所有 Node.js API。沒有 fs,有限的 crypto,沒有原生模塊。你的代碼運行在 V8 隔離區中,而非完整的 Node.js 進程。
何時使用邊緣
- 中間件(認證檢查、重定向、A/B 測試)
- 不需要數據庫連接的簡單 API 路由
- 不能被靜態緩存但可以進行個性化的頁面
何時避免邊緣
- 繁重的數據庫查詢(連接池在邊緣不能很好地工作)
- 使用 Node.js 特定庫的路由
- 任何需要超過 25ms CPU 時間的東西(邊緣函數有嚴格限制)
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 快速的基於地理位置的重定向 -- 在邊緣運行
const country = request.geo?.country;
if (country === 'DE' && !request.nextUrl.pathname.startsWith('/de')) {
return NextResponse.redirect(new URL('/de' + request.nextUrl.pathname, request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
保持中間件精簡。中間件中的每一毫秒都會添加到每個單一的頁面加載中。
數據庫和 API 層優化
連接池
無服務器函數不斷自旋上升和下降。沒有連接池,每個調用都會打開一個新的數據庫連接。規模化時,這會破壞你的數據庫。
使用連接池程序:
- PgBouncer 用於 PostgreSQL(Supabase 和 Neon 包括這個)
- Prisma Accelerate 如果你使用 Prisma(添加連接池 + 全局緩存)
- Drizzle 配合
postgres.js開箱即用地高效處理連接
查詢優化模式
// 不好 -- N+1 查詢問題
const posts = await db.post.findMany();
for (const post of posts) {
post.author = await db.user.findUnique({ where: { id: post.authorId } });
}
// 好 -- 單個帶有 join 的查詢
const posts = await db.post.findMany({
include: { author: true },
});
// 最好 -- 只選擇你需要的字段
const posts = await db.post.findMany({
select: {
id: true,
title: true,
slug: true,
author: {
select: { name: true, avatar: true },
},
},
});
並行數據獲取
這是最有影響力的模式之一,卻被犯罪地未充分使用:
// 不好 -- 順序(總時間 = 所有獲取之和)
const products = await getProducts();
const categories = await getCategories();
const banners = await getBanners();
// 好 -- 並行(總時間 = 最慢的獲取)
const [products, categories, banners] = await Promise.all([
getProducts(),
getCategories(),
getBanners(),
]);
我見過這個單一的變化將頁面加載時間縮短了一半。
渲染策略選擇
Next.js 15 給你五種渲染策略。以下是我決策的方式:
部分預渲染(PPR)
PPR 是最新和最有趣的選項。它在構建時靜態預渲染頁面的殼,然後流式傳輸動態內容。用戶看到即時靜態響應,同時個性化內容加載。
// app/page.tsx -- 啟用 PPR
import { Suspense } from 'react';
import { StaticHero } from '@/components/StaticHero';
import { PersonalizedRecommendations } from '@/components/Recommendations';
export default function HomePage() {
return (
<div>
{/* 靜態殼 -- 從 CDN 即時提供 */}
<StaticHero />
{/* 動態內容 -- 流式傳輸 */}
<Suspense fallback={<RecommendationsSkeleton />}>
<PersonalizedRecommendations />
</Suspense>
</div>
);
}
在你的配置中啟用 PPR:
// next.config.mjs
export default {
experimental: {
ppr: 'incremental',
},
};
對於電商和內容密集型站點,PPR 提供了兩全其美 -- CDN 速度的初始加載和個性化內容。
第三方腳本管理
第三方腳本是沉默的性能殺手。分析、聊天小工具、廣告跟踪器、A/B 測試工具 -- 它們迅速累積。
策略性地使用 `next/script`
import Script from 'next/script';
// 分析 -- 在頁面交互後加載
<Script
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"
strategy="afterInteractive"
/>
// 聊天小工具 -- 在空閒時加載
<Script
src="https://widget.intercom.io/widget/xxxxx"
strategy="lazyOnload"
/>
// 關鍵的 A/B 測試 -- 必須在繪製前加載
<Script
src="https://cdn.optimizely.com/js/xxxxx.js"
strategy="beforeInteractive"
/>
要無情。你添加的每個腳本都會花費你的用戶時間。我建議每季度審計第三方腳本。至少一半的時間,你會發現團隊中沒有人再使用的工具的腳本。
用於基於 Worker 的加載的 Partytown
對於非關鍵的第三方腳本,考慮 Partytown。它將腳本移動到 Web Worker,保持主線程自由:
<Script
src="https://example.com/analytics.js"
strategy="worker" // 通過 Partytown 在 Web Worker 中運行
/>
基礎設施和部署
你部署的地點和方式比大多數開發人員意識到的更重要。
2026 年 Next.js 的平台比較
| 平台 | SSR 支持 | 邊緣函數 | 冷啟動 | 起始價格 |
|---|---|---|---|---|
| Vercel | 完整 | 是(全球) | ~50ms | $20/月(專業版) |
| Cloudflare Pages | 完整(通過 OpenNext) | 是(全球) | ~10ms | $5/月 |
| AWS Amplify | 完整 | 有限 | ~200ms | 按使用付費 |
| Netlify | 完整 | 是(Deno) | ~100ms | $19/月(專業版) |
| 自託管(Docker) | 完整 | 不可用 | N/A | 服務器成本 |
| Coolify / SST | 完整 | 取決於 | ~150ms | 服務器成本 |
Vercel 仍然是 Next.js 最不費力的選擇。他們構建框架,他們為其優化平台。但 Cloudflare Pages 配合 OpenNext 在 2026 年已成為嚴肅的競爭者,特別是對於成本敏感的項目。
對於需要自託管部署的客戶,我們在 CDN 後面的 Docker 容器中獲得了良好結果。它需要更多設置,但你完全控制基礎設施。我們的 定價頁面涵蓋了不同的部署場景,如果你想討論什麼對你的項目有意義。
CDN 和邊緣緩存
無論平台如何,在所有內容前放一個 CDN。靜態資產應該有不可變的緩存標頭。ISR 頁面應該使用 stale-while-revalidate。API 響應應該在適當的地方緩存。
// 對於可以緩存的 API 路由
export async function GET() {
const data = await getPopularProducts();
return Response.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
},
});
}
真實世界基準和案例研究
以下是我們在過去一年中優化項目的實際數字:
電商網站(Shopify 無頭 + Next.js 15)
- 之前: LCP 4.2s,INP 380ms,包 487KB
- 之後: LCP 1.4s,INP 89ms,包 156KB
- 關鍵變化: 產品頁面的服務器組件,圖像優化,移除了 4 個未使用的第三方腳本,從客戶端購物車切換到服務器操作
- 業務影響: 轉換率增加 23%
SaaS 儀表板(Next.js 14 → 15 遷移)
- 之前: 初始加載 6.8s,TTI 8.2s
- 之後: 初始加載 2.1s,TTI 2.8s
- 關鍵變化: 遷移到 App Router,為數據密集型表格實現流式傳輸,為混合靜態/動態頁面添加 PPR,並行數據獲取
內容平台(無頭 CMS + Next.js)
- 之前: TTFB 890ms(SSR),LCP 3.1s
- 之後: TTFB 120ms(ISR + 邊緣),LCP 1.1s
- 關鍵變化: 從 SSR 切換到 ISR 配合按需重新驗證,部署到邊緣,優化 CMS 查詢
這些不是精選的數字。它們代表了當你系統地應用本指南中的模式時可達到的目標。
對於用 Astro 而非 Next.js 構建的項目 -- 特別是 JavaScript 要求最少的內容密集型站點 -- 數字可能更令人印象深刻。我們在我們的 Astro 開發功能中涵蓋了這一點。
常見問題
Next.js 性能優化通常花費多少? 這很大程度上取決於應用程式的大小和複雜性。對於簡單的網站,為期 1-2 週的專注優化沖刺可以實現顯著的結果。對於具有深層架構問題的大型應用程式,計劃 4-8 週的重構。ROI 通常通過改進的轉換率和降低的基礎設施成本來自我支付。如果你想要具體的估計,請通過我們的 聯繫頁面與我們聯繫。
Next.js 15 比 Next.js 14 快嗎? 是的,可衡量地。Turbopack 作為默認打包器將構建時間減少了 30-50% 並產生略微較小的包。簡化的緩存模型減少了不必要的服務器負載。而部分預渲染在正確使用時大大改進了感知性能。遷移後,我們平均看到了 15-25% 的 TTFB 改進。
在 2026 年我應該使用 Pages Router 還是 App Router? App Router,完全。Pages Router 仍然有效且仍然受支持,但所有性能創新都發生在 App Router 中。服務器組件、流式傳輸、PPR、服務器操作 -- 這些都不在 Pages Router 中可用。如果你開始一個新項目,沒有理由使用 Pages Router。
我怎樣才能快速減少 Next.js 包大小?
首先運行包分析器 -- 這正確地向你展示了重量在哪裡。然後:用較輕的替代品替換重型庫,對首屏下方組件使用動態導入,確保你從樹搖庫使用命名導入,並審計你的 'use client' 指令。這四個步驟單獨通常會將包大小減少 30-50%。
託管平台是否真的影響 Next.js 性能? 比你預期的要多。Vercel 的基礎設施專門為 Next.js 調整 -- 他們的邊緣網絡、ISR 實現和圖像優化 CDN 緊密集成。其他平台也運行良好,但你可能需要手動配置 Vercel 自動處理的東西。最大的因素是地理分佈 -- 如果你的用戶是全球的,無論平台如何,你都需要邊緣部署或 CDN。
你看到的最大的 Next.js 性能錯誤是什麼?
將所有東西都變成客戶端組件。我審計過的代碼庫中,整個頁面樹被包裝在 'use client' 中,因為開發人員在頂層需要一個 onClick 處理程序。這強制瀏覽器下載和水合所有東西,完全抵消了使 Next.js 快速的服務器組件優勢。重組你的組件樹,使客戶端組件是小的葉級節點。
部分預渲染(PPR)與常規 ISR 相比如何? ISR 在構建時生成整個頁面並定期重新驗證。PPR 在構建時預渲染靜態殼,但在請求時留下可通過流式傳輸填充的動態"孔"。PPR 更適合混合靜態和個性化內容的頁面 -- 比如描述是靜態的但推薦產品是個性化的產品頁面。初始響應與純靜態一樣快,但動態內容在沒有完整頁面加載的情況下出現。
我可以在沒有 Vercel 的情況下優化 Next.js 性能嗎? 絕對可以。本指南中的優化無論託管平台如何都有效。服務器組件、包優化、圖像優化、緩存策略、並行數據獲取 -- 這些都是應用程式級別的考慮。平台特定功能如邊緣函數和內置 ISR 支持有所不同,但 OpenNext 之類的工具使在 Cloudflare、AWS 和其他平台上運行全功能 Next.js 成為可能,具有類似的性能特徵。