Next.js 性能优化:完整的 2026 年指南

过去四年,我一直在为客户优化 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 打包器现在是开发和生产构建的默认工具。应用路由器已完全成熟。缓存行为——在 Next.js 14 中基本上让所有人都困惑——现在已经理性化了。

以下是你需要内化的内容:Next.js 为你提供了多种渲染策略,为给定页面选择错误的策略是我看到的最常见的性能错误。静态生成、服务器端渲染、增量静态再生、部分预渲染、流式传输——每种都有特定的用例。对一个一周变化一次的营销页面使用 SSR 就是在毫无理由地浪费计算资源。

性能心智模型

将 Next.js 性能视为三层:

  1. 构建时决策 ——什么被预渲染,什么是动态的,代码如何拆分
  2. 服务器时执行 ——服务器响应的速度、缓存、边缘 vs 源站
  3. 客户端体验 ——包体积、水合成本、交互就绪度

每层都与其他层相乘。快速的服务器响应在你发送 500KB 的 JavaScript 并在中端 Android 手机上花费 3 秒进行水合时毫无意义。

测量真正重要的指标

在你优化任何东西之前,你需要测量。而且你需要测量正确的东西。

Core Web Vitals 在 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 响应时间和服务器组件渲染持续时间。
# 将 bundle analyzer 添加到你的项目
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% 的组件都是客户端组件,因为开发人员习惯于旧的页面路由器思维模式。每个 '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>;
}

仅这个模式就在我们通过我们的 Next.js 开发实践 从事的项目中减少了 40-60% 的包体积。

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 年常见的包膨胀物

典型大小(gzipped) 替代品 节省体积
moment.js 72KB date-fns (tree-shakeable) ~60KB
lodash (full) 71KB Native JS / lodash-es ~65KB
chart.js 65KB lightweight-charts ~45KB
react-icons (all) 40KB+ 单个图标包 ~35KB
framer-motion 44KB motion (lite) 或 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 宽的图片。指定你的尺寸。每次都要。

视频优化

对于视频,不要使用带有巨大 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 开发实践 中构建的大部分内容),带有 webhook 触发的重新验证的 ISR 是黄金标准。内容更新在几秒内出现,无需重建整个站点。

边缘运行时和中间件性能

边缘运行时在靠近用户的 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 for PostgreSQL (Supabase and Neon include this)
  • Prisma Accelerate 如果你使用 Prisma(添加连接池 + 全局缓存)
  • Drizzle with 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"
/>

要无情。你添加的每个脚本都会让你的用户花费时间。我建议每季度审计第三方脚本。至少一半的时间,你会找到用于团队中没有人再使用的工具的脚本。

Partytown 用于基于 Worker 的加载

对于非关键的第三方脚本,考虑 Partytown。它将脚本移到 web worker,保持主线程空闲:

<Script
  src="https://example.com/analytics.js"
  strategy="worker" // 通过 Partytown 在 web worker 中运行
/>

基础设施和部署

你在哪里以及如何部署的重要性比大多数开发人员想到的要大。

2026 年 Next.js 的平台对比

平台 SSR 支持 边缘函数 冷启动 起始价格
Vercel 完整 是(全局) ~50ms $20/mo (Pro)
Cloudflare Pages 完整(通过 OpenNext) 是(全局) ~10ms $5/mo
AWS Amplify 完整 有限 ~200ms 按使用量付费
Netlify 完整 是(Deno) ~100ms $19/mo (Pro)
自托管(Docker) 完整 N/A 服务器成本
Coolify / SST 完整 取决于 ~150ms 服务器成本

对于 Next.js,Vercel 仍然是最少麻烦的途径。他们开发框架,他们为此优化平台。但 Cloudflare Pages with OpenNext 在 2026 年已成为一个严肃的竞争者,特别是对于成本敏感的项目。

对于需要自托管部署的客户,我们在 Docker 容器后面用 CDN 取得了不错的效果。这需要更多的设置,但你完全控制基础设施。如果你想讨论对你的项目有意义的内容,我们的 定价页面 涵盖了不同的部署场景。

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 Headless + 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
  • 关键改变: 迁移到应用路由器、为数据密集表实现流式传输、为混合静态/动态页面添加 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 年我应该使用页面路由器还是应用路由器? 应用路由器,完全是。页面路由器仍然有效且仍然受支持,但所有性能创新都发生在应用路由器中。服务器组件、流式传输、PPR、服务器操作——这些都不适用于页面路由器。如果你开始一个新项目,没有理由使用页面路由器。

我如何快速减少 Next.js 包体积? 首先运行 bundle analyzer——这准确显示了重量在哪里。然后:用更轻的替代品替换重库,对屏幕外组件使用动态导入,确保你从可树摇的库使用命名导入,审计你的 '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 成为可能,具有类似的性能特征。