nextjs-performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNext.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 for faster loading
next/image - Configure font optimization with to eliminate layout shift
next/font - Set up caching strategies using ,
unstable_cache, or ISRrevalidateTag - 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或ISR设置缓存策略revalidateTag - 将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, ISR)revalidateTag - 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、ISR)revalidateTag - Server Components模式及客户端组件转服务端组件
- 流式传输与Suspense实现渐进式加载
- 包体积优化与代码分割
- 元数据与SEO配置
- 路由处理器优化
- Next.js 16 + React 19模式
Instructions
使用说明
Before Starting
开始前准备
- Analyze current performance with Lighthouse
- Identify bottlenecks - check Core Web Vitals in Chrome DevTools or PageSpeed Insights
- Determine optimization priority:
- LCP issues → Focus on images, fonts
- INP issues → Reduce JS, use Server Components
- CLS issues → Add dimensions, use next/font
- 用Lighthouse分析当前性能
- 识别性能瓶颈 - 在Chrome DevTools或PageSpeed Insights中查看Core Web Vitals数据
- 确定优化优先级:
- LCP问题 → 重点优化图片、字体
- INP问题 → 减少JS代码,使用Server Components
- CLS问题 → 添加尺寸属性,使用next/font
How to Use This Skill
如何使用本技能
-
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
- Image issues →
-
Follow the quick patterns for common optimizations
-
Apply before/after conversions to improve existing code
-
Verify improvements with Lighthouse after changes
-
根据优化领域加载相关参考文件:
- 图片问题 →
references/image-optimization.md - 字体/布局偏移 →
references/font-optimization.md - 缓存 →
references/caching-strategies.md - 组件架构 →
references/server-components.md
- 图片问题 →
-
遵循常见优化的快速模式
-
应用前后代码转换以改进现有代码
-
修改后用Lighthouse验证优化效果
Core Principles
核心原则
- Prefer Server Components - Only use 'use client' when necessary (browser APIs, interactivity)
- Load components as low as possible - Keep Client Components at leaf nodes
- Use Suspense boundaries - Enable streaming and progressive loading
- Cache appropriately - Use tags for granular revalidation
- Measure before/after - Always verify improvements with real metrics
- 优先使用Server Components - 仅在必要时使用'use client'(如浏览器API、交互逻辑)
- 尽可能晚加载组件 - 将Client Components放在叶子节点
- 使用Suspense边界 - 启用流式传输与渐进式加载
- 合理缓存 - 使用标签实现细粒度重新验证
- 前后对比测量 - 始终用真实指标验证优化效果
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:
| Topic | Reference File |
|---|---|
| Core Web Vitals | |
| Image Optimization | |
| Font Optimization | |
| Caching Strategies | |
| Server Components | |
| Streaming/Suspense | |
| Bundle Optimization | |
| Metadata/SEO | |
| API Routes | |
| Next.js 16 Patterns | |
处理特定领域优化时加载以下参考文件:
| 主题 | 参考文件 |
|---|---|
| Core Web Vitals | |
| 图片优化 | |
| 字体优化 | |
| 缓存策略 | |
| Server Components | |
| 流式传输/Suspense | |
| 包体积优化 | |
| 元数据/SEO | |
| API路由 | |
| Next.js 16模式 | |
Common Conversions
常见代码转换
| From | To | Benefit |
|---|---|---|
| Direct async in Server Component | -70% JS, faster TTFB |
| Server Component with direct DB access | Simpler code, no hydration |
| Client-side fetch | | Faster repeated loads |
| | Optimized formats, lazy loading |
| CSS font import | | Zero CLS, automatic optimization |
| Static import of heavy component | | Reduced initial bundle |
| 转换前 | 转换后 | 收益 |
|---|---|---|
| 服务端组件中直接异步获取 | JS体积减少70%,TTFB更快 |
用 | 服务端组件直接访问数据库 | 代码更简洁,无需 hydration |
| 客户端fetch | | 重复加载速度更快 |
| | 优化格式,支持懒加载 |
| CSS字体导入 | | 零CLS,自动优化 |
| 静态导入重型组件 | | 减小初始包体积 |
Best Practices
最佳实践
Images
图片
- Use for all images
next/image - Add to LCP images only
priority - Provide and
widthorheightwith sizesfill - Use for better UX
placeholder="blur" - Configure remotePatterns in next.config.js
- 所有图片使用
next/image - 仅为LCP图片添加属性
priority - 提供和
width,或使用height并指定sizesfill - 使用提升用户体验
placeholder="blur" - 在next.config.js中配置remotePatterns
Fonts
字体
- Use instead of CSS imports
next/font - Specify to reduce size
subsets - Use for immediate text render
display: 'swap' - Create CSS variable with option
variable - Configure Tailwind to use CSS variables
- 使用替代CSS导入
next/font - 指定以减小体积
subsets - 使用实现文本立即渲染
display: 'swap' - 用选项创建CSS变量
variable - 配置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
图片优化限制
- should only be used for above-the-fold images
priority - External images require configuration in next.config.js
- and
widthare required unless usingheightfill - Animated GIFs are not optimized by default
- 仅应用于首屏图片
priority - 外部图片需在next.config.js中配置
- 除非使用,否则必须提供
fill和widthheight - 动态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
undefinedbash
undefinedInstall 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 with proper dimensions
next/image - LCP images have attribute
priority - Fonts use with subsets
next/font - 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 - 字体使用并配置subsets
next/font - 尽可能使用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 oftentsx
// ❌ 错误:在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 } // 评论数据经常变更