我花了過去四年時間為客戶優化 Next.js 應用程式,從年營收 5000 萬美元的電商商店到擁有 10 萬多日活躍用戶的 SaaS 儀表板。我學到的一些東西與文檔完全吻合。但大部分情況並非如此。這是我希望在開始時就有人交給我的指南 -- 已針對 Next.js 15 和 2026 年實際重要的模式進行了更新。

性能不是你在最後才加上的功能。它是你從第一天開始做出的一系列決策,每個決策都會產生複合效應。錯過幾個早期決策,你就在看著需要重寫。把它們做對,你的應用程式就像運行在用戶本地機器上一樣。

讓我們開始吧。

目錄

Next.js Performance Optimization: The Complete 2026 Guide

理解 Next.js 15 性能基礎知識

Next.js 15(自 2025 年末穩定)在底層性能工作原理方面帶來了一些重大變化。Turbopack 打包器現在是開發和生產構建的默認選項。App Router 已完全成熟。而令 Next.js 14 中基本所有人都困惑的緩存行為已經得到理性化處理。

你需要內化的是:Next.js 為你提供了多種渲染策略,為給定頁面選擇錯誤的策略是我看到的最常見的性能錯誤。靜態生成、服務器端渲染、增量靜態再生成、部分預渲染、流式傳輸 -- 每種都有特定的用例。對於每週變化一次的營銷頁面使用 SSR 只是在徒勞地浪費計算資源。

性能心智模型

將 Next.js 性能視為三層:

  1. 構建時決策 -- 什麼被預渲染,什麼是動態的,代碼如何分割
  2. 服務器時執行 -- 你的服務器響應速度有多快,緩存,邊緣與原點
  3. 客戶端體驗 -- 包大小,水合成本,交互就緒性

每層都會乘以其他層。快速的服務器響應在你向中端安卓手機發送 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 Performance Optimization: The Complete 2026 Guide - architecture

包大小優化

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 年,舉動是:

  1. 使用 FFmpeg 或 Mux 之類的服務轉碼為多個質量
  2. 對任何超過 10 秒的視頻使用 HLS 流式傳輸
  3. 對於短動畫,考慮 WebM 甚至動畫 AVIF
  4. 使用 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 成為可能,具有類似的性能特徵。