nextjs-performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.js Performance Optimization

Next.js 性能优化

Expert guidance for optimizing Next.js applications with focus on Core Web Vitals, modern patterns, and best practices.
针对Next.js应用优化的专业指导,重点关注Core Web Vitals、现代模式与最佳实践。

Overview

概述

This skill provides comprehensive guidance for optimizing Next.js applications. It covers Core Web Vitals optimization (LCP, INP, CLS), modern React patterns, Server Components, caching strategies, and bundle optimization techniques. Designed for developers already familiar with React/Next.js who want to implement production-grade optimizations.
本技能为Next.js应用优化提供全面指导,涵盖Core Web Vitals优化(LCP、INP、CLS)、现代React模式、Server Components、缓存策略及包体积优化技术。专为已熟悉React/Next.js、希望实现生产级优化的开发者设计。

When to Use

适用场景

Use this skill when working on Next.js applications and need to:
  • Optimize Core Web Vitals (LCP, INP, CLS) for better performance and SEO
  • Implement image optimization with
    next/image
    for faster loading
  • Configure font optimization with
    next/font
    to eliminate layout shift
  • Set up caching strategies using
    unstable_cache
    ,
    revalidateTag
    , or ISR
  • Convert Client Components to Server Components for reduced bundle size
  • Implement Suspense streaming for progressive page loading
  • Analyze and reduce bundle size with code splitting and dynamic imports
  • Configure metadata and SEO for better search engine visibility
  • Optimize API route handlers for better performance
  • Apply Next.js 16 and React 19 modern patterns
在开发Next.js应用时,若你需要完成以下操作,可使用本技能:
  • 优化Core Web Vitals(LCP、INP、CLS)以提升性能与SEO表现
  • 实现
    next/image
    图片优化,加快加载速度
  • 配置
    next/font
    字体优化,消除布局偏移
  • 使用
    unstable_cache
    revalidateTag
    或ISR设置缓存策略
  • 将Client Components转换为Server Components以减小包体积
  • 实现Suspense流式传输,渐进式加载页面
  • 通过代码分割与动态导入分析并减小包体积
  • 配置元数据与SEO,提升搜索引擎可见性
  • 优化API路由处理器,提升性能
  • 应用Next.js 16与React 19的现代模式

Coverage Areas

覆盖领域

  • Core Web Vitals optimization (LCP, INP, CLS)
  • Image optimization with
    next/image
  • Font optimization with
    next/font
  • Caching strategies (
    unstable_cache
    ,
    revalidateTag
    , ISR)
  • Server Components patterns and Client-to-Server conversion
  • Streaming and Suspense for progressive loading
  • Bundle optimization and code splitting
  • Metadata and SEO configuration
  • Route handlers optimization
  • Next.js 16 + React 19 patterns
  • Core Web Vitals优化(LCP、INP、CLS)
  • 基于
    next/image
    的图片优化
  • 基于
    next/font
    的字体优化
  • 缓存策略
    unstable_cache
    revalidateTag
    、ISR)
  • Server Components模式及客户端组件转服务端组件
  • 流式传输与Suspense实现渐进式加载
  • 包体积优化与代码分割
  • 元数据与SEO配置
  • 路由处理器优化
  • Next.js 16 + React 19模式

Instructions

使用说明

Before Starting

开始前准备

  1. Analyze current performance with Lighthouse
  2. Identify bottlenecks - check Core Web Vitals in Chrome DevTools or PageSpeed Insights
  3. Determine optimization priority:
    • LCP issues → Focus on images, fonts
    • INP issues → Reduce JS, use Server Components
    • CLS issues → Add dimensions, use next/font
  1. 用Lighthouse分析当前性能
  2. 识别性能瓶颈 - 在Chrome DevTools或PageSpeed Insights中查看Core Web Vitals数据
  3. 确定优化优先级
    • LCP问题 → 重点优化图片、字体
    • INP问题 → 减少JS代码,使用Server Components
    • CLS问题 → 添加尺寸属性,使用next/font

How to Use This Skill

如何使用本技能

  1. Load relevant reference files based on the area you're optimizing:
    • Image issues →
      references/image-optimization.md
    • Font/layout shift →
      references/font-optimization.md
    • Caching →
      references/caching-strategies.md
    • Component architecture →
      references/server-components.md
  2. Follow the quick patterns for common optimizations
  3. Apply before/after conversions to improve existing code
  4. Verify improvements with Lighthouse after changes
  1. 根据优化领域加载相关参考文件
    • 图片问题 →
      references/image-optimization.md
    • 字体/布局偏移 →
      references/font-optimization.md
    • 缓存 →
      references/caching-strategies.md
    • 组件架构 →
      references/server-components.md
  2. 遵循常见优化的快速模式
  3. 应用前后代码转换以改进现有代码
  4. 修改后用Lighthouse验证优化效果

Core Principles

核心原则

  1. Prefer Server Components - Only use 'use client' when necessary (browser APIs, interactivity)
  2. Load components as low as possible - Keep Client Components at leaf nodes
  3. Use Suspense boundaries - Enable streaming and progressive loading
  4. Cache appropriately - Use tags for granular revalidation
  5. Measure before/after - Always verify improvements with real metrics
  1. 优先使用Server Components - 仅在必要时使用'use client'(如浏览器API、交互逻辑)
  2. 尽可能晚加载组件 - 将Client Components放在叶子节点
  3. 使用Suspense边界 - 启用流式传输与渐进式加载
  4. 合理缓存 - 使用标签实现细粒度重新验证
  5. 前后对比测量 - 始终用真实指标验证优化效果

Examples

示例

Example 1: Convert Client Component to Server Component

示例1:将客户端组件转换为服务端组件

BEFORE (Client Component with useEffect):
tsx
'use client'
import { useEffect, useState } from 'react'

export default function ProductList() {
  const [products, setProducts] = useState([])

  useEffect(() => {
    fetch('/api/products').then(r => r.json()).then(setProducts)
  }, [])

  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
AFTER (Server Component with direct data access):
tsx
import { db } from '@/lib/db'

export default async function ProductList() {
  const products = await db.product.findMany()
  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
转换前(含useEffect的客户端组件):
tsx
'use client'
import { useEffect, useState } from 'react'

export default function ProductList() {
  const [products, setProducts] = useState([])

  useEffect(() => {
    fetch('/api/products').then(r => r.json()).then(setProducts)
  }, [])

  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
转换后(直接获取数据的服务端组件):
tsx
import { db } from '@/lib/db'

export default async function ProductList() {
  const products = await db.product.findMany()
  return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}

Example 2: Optimize Images for LCP

示例2:为LCP优化图片

tsx
import Image from 'next/image'

export function Hero() {
  return (
    <div className="relative w-full h-[600px]">
      <Image
        src="/hero.jpg"
        alt="Hero"
        fill
        priority          // Disable lazy loading for LCP
        sizes="100vw"
        className="object-cover"
      />
    </div>
  )
}
tsx
import Image from 'next/image'

export function Hero() {
  return (
    <div className="relative w-full h-[600px]">
      <Image
        src="/hero.jpg"
        alt="Hero"
        fill
        priority          // 为LCP图片禁用懒加载
        sizes="100vw"
        className="object-cover"
      />
    </div>
  )
}

Example 3: Implement Caching Strategy

示例3:实现缓存策略

tsx
import { unstable_cache, revalidateTag } from 'next/cache'

// Cached data function
const getProducts = unstable_cache(
  async () => db.product.findMany(),
  ['products'],
  { revalidate: 3600, tags: ['products'] }
)

// Revalidate on mutation
export async function createProduct(data: FormData) {
  'use server'
  await db.product.create({ data })
  revalidateTag('products')
}
tsx
import { unstable_cache, revalidateTag } from 'next/cache'

// 缓存数据函数
const getProducts = unstable_cache(
  async () => db.product.findMany(),
  ['products'],
  { revalidate: 3600, tags: ['products'] }
)

// 数据变更时重新验证
export async function createProduct(data: FormData) {
  'use server'
  await db.product.create({ data })
  revalidateTag('products')
}

Example 4: Setup Optimized Fonts

示例4:设置优化后的字体

tsx
import { Inter } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.variable}>
      <body className={`${inter.className} antialiased`}>
        {children}
      </body>
    </html>
  )
}
tsx
import { Inter } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.variable}>
      <body className={`${inter.className} antialiased`}>
        {children}
      </body>
    </html>
  )
}

Example 5: Implement Suspense Streaming

示例5:实现Suspense流式传输

tsx
import { Suspense } from 'react'

export default function Page() {
  return (
    <>
      <header>Static content (immediate)</header>

      <Suspense fallback={<ProductSkeleton />}>
        <ProductList />  {/* Streamed when ready */}
      </Suspense>

      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews />      {/* Independent streaming */}
      </Suspense>
    </>
  )
}
tsx
import { Suspense } from 'react'

export default function Page() {
  return (
    <>
      <header>静态内容(立即加载)</header>

      <Suspense fallback={<ProductSkeleton />}>
        <ProductList />  {/* 准备就绪后流式传输 */}
      </Suspense>

      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews />      {/* 独立流式传输 */}
      </Suspense>
    </>
  )
}

Reference Documentation

参考文档

Load these references when working on specific areas:
TopicReference File
Core Web Vitals
references/core-web-vitals.md
Image Optimization
references/image-optimization.md
Font Optimization
references/font-optimization.md
Caching Strategies
references/caching-strategies.md
Server Components
references/server-components.md
Streaming/Suspense
references/streaming-suspense.md
Bundle Optimization
references/bundle-optimization.md
Metadata/SEO
references/metadata-seo.md
API Routes
references/api-routes.md
Next.js 16 Patterns
references/nextjs-16-patterns.md
处理特定领域优化时加载以下参考文件:
主题参考文件
Core Web Vitals
references/core-web-vitals.md
图片优化
references/image-optimization.md
字体优化
references/font-optimization.md
缓存策略
references/caching-strategies.md
Server Components
references/server-components.md
流式传输/Suspense
references/streaming-suspense.md
包体积优化
references/bundle-optimization.md
元数据/SEO
references/metadata-seo.md
API路由
references/api-routes.md
Next.js 16模式
references/nextjs-16-patterns.md

Common Conversions

常见代码转换

FromToBenefit
useEffect
+ fetch
Direct async in Server Component-70% JS, faster TTFB
useState
for data
Server Component with direct DB accessSimpler code, no hydration
Client-side fetch
unstable_cache
or ISR
Faster repeated loads
img
tag
next/image
Optimized formats, lazy loading
CSS font import
next/font
Zero CLS, automatic optimization
Static import of heavy component
dynamic()
Reduced initial bundle
转换前转换后收益
useEffect
+ fetch
服务端组件中直接异步获取JS体积减少70%,TTFB更快
useState
存储数据
服务端组件直接访问数据库代码更简洁,无需 hydration
客户端fetch
unstable_cache
或ISR
重复加载速度更快
img
标签
next/image
优化格式,支持懒加载
CSS字体导入
next/font
零CLS,自动优化
静态导入重型组件
dynamic()
减小初始包体积

Best Practices

最佳实践

Images

图片

  • Use
    next/image
    for all images
  • Add
    priority
    to LCP images only
  • Provide
    width
    and
    height
    or
    fill
    with sizes
  • Use
    placeholder="blur"
    for better UX
  • Configure remotePatterns in next.config.js
  • 所有图片使用
    next/image
  • 仅为LCP图片添加
    priority
    属性
  • 提供
    width
    height
    ,或使用
    fill
    并指定sizes
  • 使用
    placeholder="blur"
    提升用户体验
  • 在next.config.js中配置remotePatterns

Fonts

字体

  • Use
    next/font
    instead of CSS imports
  • Specify
    subsets
    to reduce size
  • Use
    display: 'swap'
    for immediate text render
  • Create CSS variable with
    variable
    option
  • Configure Tailwind to use CSS variables
  • 使用
    next/font
    替代CSS导入
  • 指定
    subsets
    以减小体积
  • 使用
    display: 'swap'
    实现文本立即渲染
  • variable
    选项创建CSS变量
  • 配置Tailwind使用CSS变量

Caching

缓存

  • Cache expensive queries with
    unstable_cache
  • Use meaningful cache tags for granular control
  • Implement on-demand revalidation for dynamic content
  • Set TTL based on data change frequency
  • Use revalidatePath for route-level invalidation
  • unstable_cache
    缓存耗时查询
  • 使用有意义的缓存标签实现细粒度控制
  • 为动态内容实现按需重新验证
  • 根据数据变更频率设置TTL
  • 用revalidatePath实现路由级别的失效

Components

组件

  • Convert Client Components to Server Components where possible
  • Keep Client Components at the leaf level
  • Use Suspense boundaries for progressive loading
  • Implement proper loading states
  • Use dynamic() for heavy components below the fold
  • 尽可能将客户端组件转换为服务端组件
  • 将客户端组件放在叶子节点
  • 为数据获取使用Suspense边界
  • 实现合适的加载状态
  • 对可视区域外的重型组件使用dynamic()懒加载

Bundle

包体积

  • Lazy load heavy components with
    dynamic()
  • Use named exports for better tree shaking
  • Analyze bundle regularly with @next/bundle-analyzer
  • Prefer ESM packages over CommonJS
  • Use modularizeImports for large libraries
  • dynamic()
    懒加载重型组件
  • 使用具名导出以优化Tree Shaking
  • 定期用@next/bundle-analyzer分析包体积
  • 优先选择ESM包而非CommonJS
  • 对大型库使用modularizeImports

Constraints and Warnings

限制与注意事项

Server Components Limitations

Server Components限制

  • Cannot use browser APIs (window, localStorage, document)
  • Cannot use React hooks (useState, useEffect, useContext)
  • Cannot use event handlers (onClick, onSubmit)
  • Cannot use dynamic imports with ssr: false
  • 无法使用浏览器API(window、localStorage、document)
  • 无法使用React Hooks(useState、useEffect、useContext)
  • 无法使用事件处理器(onClick、onSubmit)
  • 无法使用ssr: false的动态导入

Image Optimization Constraints

图片优化限制

  • priority
    should only be used for above-the-fold images
  • External images require configuration in next.config.js
  • width
    and
    height
    are required unless using
    fill
  • Animated GIFs are not optimized by default
  • priority
    仅应用于首屏图片
  • 外部图片需在next.config.js中配置
  • 除非使用
    fill
    ,否则必须提供
    width
    height
  • 动态GIF默认不会被优化

Caching Considerations

缓存注意事项

  • Cache tags must be manually invalidated
  • Data cache is per-request in development
  • Edge runtime has different caching behavior
  • Be careful caching user-specific data
  • 缓存标签需手动失效
  • 开发环境中数据缓存按请求隔离
  • Edge Runtime的缓存行为不同
  • 缓存用户特定数据时需谨慎

Bundle Size Warnings

包体积警告

  • Dynamic imports can impact SEO if critical content
  • Tree shaking requires proper ES module usage
  • Some libraries cannot be tree shaken (avoid barrel exports)
  • Client Components increase bundle size - use sparingly
  • 动态导入可能影响关键内容的SEO
  • Tree Shaking需要正确使用ES模块
  • 部分库无法被Tree Shaking(避免桶式导出)
  • 客户端组件会增加包体积 - 谨慎使用

Next.js 16 + React 19 Specifics

Next.js 16 + React 19 特性

Async Params

异步Params

tsx
// Next.js 15+ params is a Promise
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await fetchPost(slug)
  return <article>{post.content}</article>
}
tsx
// Next.js 15+ 中params是Promise
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await fetchPost(slug)
  return <article>{post.content}</article>
}

use() Hook for Promises

用于Promise的use() Hook

tsx
'use client'
import { use, Suspense } from 'react'

function Comments({ promise }: { promise: Promise<Comment[]> }) {
  const comments = use(promise)  // Suspend until resolved
  return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}
tsx
'use client'
import { use, Suspense } from 'react'

function Comments({ promise }: { promise: Promise<Comment[]> }) {
  const comments = use(promise)  // 等待Promise解析
  return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}

useOptimistic for UI Updates

用于UI更新的useOptimistic

tsx
'use client'
import { useOptimistic } from 'react'

export function TodoList({ todos }: { todos: Todo[] }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo: Todo) => [...state, newTodo]
  )

  async function addTodo(formData: FormData) {
    const text = formData.get('text') as string
    addOptimisticTodo({ id: crypto.randomUUID(), text, completed: false })
    await createTodo(text)
  }

  return (
    <form action={addTodo}>
      <input name="text" />
      {optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
    </form>
  )
}
tsx
'use client'
import { useOptimistic } from 'react'

export function TodoList({ todos }: { todos: Todo[] }) {
  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo: Todo) => [...state, newTodo]
  )

  async function addTodo(formData: FormData) {
    const text = formData.get('text') as string
    addOptimisticTodo({ id: crypto.randomUUID(), text, completed: false })
    await createTodo(text)
  }

  return (
    <form action={addTodo}>
      <input name="text" />
      {optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
    </form>
  )
}

Bundle Analysis

包体积分析

bash
undefined
bash
undefined

Install analyzer

安装分析工具

npm install --save-dev @next/bundle-analyzer
npm install --save-dev @next/bundle-analyzer

Run analysis

运行分析

ANALYZE=true npm run build

```javascript
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  modularizeImports: {
    'lodash': { transform: 'lodash/{{member}}' },
  },
})
ANALYZE=true npm run build

```javascript
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

module.exports = withBundleAnalyzer({
  modularizeImports: {
    'lodash': { transform: 'lodash/{{member}}' },
  },
})

Performance Checklist

性能检查清单

  • All images use
    next/image
    with proper dimensions
  • LCP images have
    priority
    attribute
  • Fonts use
    next/font
    with subsets
  • Server Components used where possible
  • Client Components at leaf level only
  • Suspense boundaries for data fetching
  • Caching configured for expensive operations
  • Bundle analyzed for duplicates
  • Heavy components lazy loaded
  • Lighthouse score verified before/after
  • 所有图片使用
    next/image
    并配置正确尺寸
  • LCP图片添加
    priority
    属性
  • 字体使用
    next/font
    并配置subsets
  • 尽可能使用Server Components
  • 客户端组件仅放在叶子节点
  • 为数据获取添加Suspense边界
  • 为耗时操作配置缓存
  • 分析包体积查找重复依赖
  • 重型组件已懒加载
  • 优化前后均验证Lighthouse分数

Common Mistakes

常见错误

tsx
// ❌ DON'T: Fetch in useEffect
'use client'
useEffect(() => { fetch('/api/data').then(...) }, [])

// ✅ DO: Fetch directly in Server Component
const data = await fetch('/api/data')

// ❌ DON'T: Forget dimensions on images
<Image src="/photo.jpg" />

// ✅ DO: Always provide dimensions
<Image src="/photo.jpg" width={800} height={600} />

// ❌ DON'T: Use priority on all images
<Image src="/photo1.jpg" priority />
<Image src="/photo2.jpg" priority />

// ✅ DO: Priority only for LCP
<Image src="/hero.jpg" priority />
<Image src="/photo.jpg" loading="lazy" />

// ❌ DON'T: Cache everything with same TTL
{ revalidate: 3600 }

// ✅ DO: Match TTL to data change frequency
{ revalidate: 86400 } // Categories rarely change
{ revalidate: 60 }     // Comments change often
tsx
// ❌ 错误:在useEffect中获取数据
'use client'
useEffect(() => { fetch('/api/data').then(...) }, [])

// ✅ 正确:在服务端组件中直接获取数据
const data = await fetch('/api/data')

// ❌ 错误:图片未设置尺寸
<Image src="/photo.jpg" />

// ✅ 正确:始终提供尺寸
<Image src="/photo.jpg" width={800} height={600} />

// ❌ 错误:为所有图片添加priority
<Image src="/photo1.jpg" priority />
<Image src="/photo2.jpg" priority />

// ✅ 正确:仅为LCP图片添加priority
<Image src="/hero.jpg" priority />
<Image src="/photo.jpg" loading="lazy" />

// ❌ 错误:所有缓存使用相同TTL
{ revalidate: 3600 }

// ✅ 正确:根据数据变更频率设置TTL
{ revalidate: 86400 } // 分类数据很少变更
{ revalidate: 60 }     // 评论数据经常变更

External Resources

外部资源