性能优化 #

性能优化概述 #

Vercel 提供多层次的性能优化:

text
┌─────────────────────────────────────────┐
│        性能优化层次                      │
├─────────────────────────────────────────┤
│  边缘层      →  CDN 缓存、边缘函数       │
│  构建层      →  代码分割、资源优化       │
│  运行时层    →  服务端渲染、流式传输     │
│  客户端层    →  懒加载、预加载           │
└─────────────────────────────────────────┘

缓存策略 #

静态资源缓存 #

json
{
  "headers": [
    {
      "source": "/static/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    },
    {
      "source": "/(.*).js",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    },
    {
      "source": "/(.*).css",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

API 缓存 #

typescript
import { NextResponse } from 'next/server'

export async function GET() {
  const data = await fetchData()
  
  return NextResponse.json(data, {
    headers: {
      'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
    },
  })
}

缓存指令说明 #

text
Cache-Control 值:
├── public              →  可被任何缓存存储
├── private             →  仅浏览器缓存
├── max-age=3600        →  缓存 1 小时
├── s-maxage=3600       →  CDN 缓存 1 小时
├── stale-while-revalidate=60  →  过期后 60 秒内可使用旧响应
├── immutable           →  永不过期
└── no-cache            →  每次验证

边缘缓存 #

ISR(增量静态再生) #

typescript
export const revalidate = 60

export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }
  }).then(res => res.json())
  
  return <div>{data.title}</div>
}

按需重新验证 #

typescript
import { revalidatePath, revalidateTag } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
  const path = request.nextUrl.searchParams.get('path')
  
  if (path) {
    revalidatePath(path)
    return NextResponse.json({ revalidated: true, now: Date.now() })
  }
  
  revalidateTag('collection')
  return NextResponse.json({ revalidated: true, now: Date.now() })
}

缓存标签 #

typescript
export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { tags: ['collection'] }
  }).then(res => res.json())
  
  return <div>{data.title}</div>
}

构建优化 #

代码分割 #

typescript
import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(() => import('@/components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false,
})

export default function Page() {
  return (
    <div>
      <DynamicComponent />
    </div>
  )
}

Tree Shaking #

javascript
import { debounce, throttle } from 'lodash-es'
import { format } from 'date-fns'

包体积分析 #

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

module.exports = withBundleAnalyzer({
  // next config
})
bash
ANALYZE=true npm run build

外部依赖优化 #

javascript
module.exports = {
  experimental: {
    optimizePackageImports: ['lodash', 'date-fns', '@mui/material'],
  },
}

运行时优化 #

流式渲染 #

typescript
import { Suspense } from 'react'

async function SlowComponent() {
  const data = await fetch('https://api.example.com/slow')
  return <div>{data}</div>
}

export default function Page() {
  return (
    <div>
      <h1>Fast Content</h1>
      <Suspense fallback={<p>Loading...</p>}>
        <SlowComponent />
      </Suspense>
    </div>
  )
}

并行数据获取 #

typescript
async function Page() {
  const [users, posts, comments] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json()),
  ])
  
  return (
    <div>
      <UsersList users={users} />
      <PostsList posts={posts} />
      <CommentsList comments={comments} />
    </div>
  )
}

预加载资源 #

tsx
import Link from 'next/link'

export default function Page() {
  return (
    <div>
      <Link href="/about" prefetch>
        About
      </Link>
    </div>
  )
}

图片优化 #

tsx
import Image from 'next/image'

export default function Page() {
  return (
    <>
      <Image
        src="/hero.jpg"
        alt="Hero"
        width={800}
        height={600}
        priority
      />
      
      <Image
        src="/gallery/1.jpg"
        alt="Gallery"
        width={400}
        height={300}
        loading="lazy"
      />
    </>
  )
}

字体优化 #

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

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

export default function Layout({ children }) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

客户端优化 #

懒加载组件 #

tsx
import { lazy, Suspense } from 'react'

const HeavyComponent = lazy(() => import('./Heavy'))

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  )
}

虚拟列表 #

bash
npm install react-window
tsx
import { FixedSizeList } from 'react-window'

const Row = ({ index, style }) => (
  <div style={style}>Item {index}</div>
)

export default function VirtualList({ items }) {
  return (
    <FixedSizeList
      height={400}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  )
}

Service Worker #

javascript
public/sw.js

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request)
    })
  )
})

数据库优化 #

连接池 #

typescript
import { Pool } from 'pg'

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 1,
})

export async function query(text: string, params?: any[]) {
  const client = await pool.connect()
  try {
    return await client.query(text, params)
  } finally {
    client.release()
  }
}

查询优化 #

typescript
import { prisma } from '@/lib/prisma'

export async function getUsers() {
  return prisma.user.findMany({
    select: {
      id: true,
      name: true,
      email: true,
    },
    where: {
      active: true,
    },
    take: 20,
  })
}

性能预算 #

配置性能预算 #

json
{
  "budgets": [
    {
      "resourceType": "script",
      "budget": 300
    },
    {
      "resourceType": "stylesheet",
      "budget": 100
    },
    {
      "resourceType": "image",
      "budget": 500
    },
    {
      "resourceType": "total",
      "budget": 1000
    }
  ]
}

Lighthouse CI #

yaml
.github/workflows/lighthouse.yml

name: Lighthouse CI
on: [push]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: treosh/lighthouse-ci-action@v10
        with:
          urls: |
            https://your-site.vercel.app
          budgetPath: ./budget.json

性能监控 #

Web Vitals 监控 #

typescript
import { useReportWebVitals } from 'next/web-vitals'

export function WebVitals() {
  useReportWebVitals((metric) => {
    if (metric.name === 'LCP' && metric.value > 2500) {
      console.warn('LCP is slow:', metric.value)
    }
  })
}

性能标记 #

typescript
export default function Page() {
  performance.mark('page-start')
  
  // ... 组件代码
  
  performance.mark('page-end')
  performance.measure('page-render', 'page-start', 'page-end')
  
  return <div>Content</div>
}

最佳实践 #

性能优化清单 #

text
┌─────────────────────────────────────────┐
│        性能优化清单                      │
├─────────────────────────────────────────┤
│  ✓ 启用静态资源缓存                     │
│  ✓ 使用 ISR 减少服务器负载              │
│  ✓ 代码分割减少包体积                   │
│  ✓ 图片自动优化                         │
│  ✓ 字体预加载                           │
│  ✓ 使用流式渲染                         │
│  ✓ 并行数据获取                         │
│  ✓ 设置性能预算                         │
│  ✓ 监控 Web Vitals                      │
└─────────────────────────────────────────┘

Core Web Vitals 目标 #

text
LCP < 2.5s
FID < 100ms
CLS < 0.1
INP < 200ms
TTFB < 600ms

下一步 #

了解性能优化后,接下来学习 故障排查 掌握问题排查技巧!

最后更新:2026-03-28