Next.js 提供了多种性能优化技术,帮助开发者构建高性能的 Web 应用。以下是 Next.js 的主要性能优化策略:
1. 自动代码分割
Next.js 自动将代码分割成小块,只加载当前页面所需的代码。
javascript// pages/index.js import dynamic from 'next/dynamic'; // 动态导入组件 const DynamicComponent = dynamic(() => import('../components/HeavyComponent'), { loading: () => <p>Loading...</p>, ssr: false // 禁用服务器端渲染 }); export default function Home() { return ( <div> <h1>Home Page</h1> <DynamicComponent /> </div> ); }
2. 图片优化
使用 next/image 组件自动优化图片。
javascriptimport Image from 'next/image'; export default function ImageExample() { return ( <Image src="/hero.jpg" alt="Hero image" width={800} height={600} priority // 首屏图片使用优先加载 placeholder="blur" // 模糊占位符 blurDataURL="data:image/jpeg;base64,..." /> ); }
图片优化特性:
- 自动选择最佳格式(WebP、AVIF)
- 响应式图片
- 懒加载
- 避免布局偏移
3. 字体优化
使用 next/font 优化字体加载。
javascriptimport { 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>{children}</body> </html> ); }
4. 数据获取优化
使用缓存和 ISR
javascript// 使用 fetch 的缓存选项 async function Page() { const data = await fetch('https://api.example.com/data', { next: { revalidate: 60, // ISR:每 60 秒重新验证 tags: ['data'] // 标签用于按需重新验证 } }).then(r => r.json()); return <div>{data.content}</div>; }
使用 React Query 或 SWR
javascript'use client'; import useSWR from 'swr'; const fetcher = (url) => fetch(url).then(r => r.json()); export default function DataComponent() { const { data, error, isLoading } = useSWR('/api/data', fetcher, { revalidateOnFocus: false, dedupingInterval: 60000, }); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{data.content}</div>; }
5. 预加载和预取
javascriptimport Link from 'next/link'; export default function Navigation() { return ( <nav> <Link href="/about" prefetch={true}> About </Link> <Link href="/contact" prefetch={false}> Contact </Link> </nav> ); }
6. 脚本优化
使用 next/script 优化第三方脚本加载。
javascriptimport Script from 'next/script'; export default function Page() { return ( <> <Script src="https://www.googletagmanager.com/gtag/js" strategy="afterInteractive" /> <Script id="google-analytics" strategy="afterInteractive"> {` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'GA_MEASUREMENT_ID'); `} </Script> </> ); }
脚本加载策略:
beforeInteractive:在页面交互前加载afterInteractive:在页面可交互后立即加载lazyOnload:在浏览器空闲时加载
7. 使用 React.memo 和 useMemo
javascript'use client'; import { memo, useMemo } from 'react'; const ExpensiveComponent = memo(function ExpensiveComponent({ data }) { const processedData = useMemo(() => { return data.map(item => ({ ...item, computed: expensiveCalculation(item) })); }, [data]); return <div>{/* 渲染处理后的数据 */}</div>; });
8. 虚拟化长列表
javascript'use client'; import { useVirtualizer } from '@tanstack/react-virtual'; export default function VirtualList({ items }) { const parentRef = useRef(); const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, }); return ( <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}> <div style={{ height: `${virtualizer.getTotalSize()}px` }}> {virtualizer.getVirtualItems().map(virtualItem => ( <div key={virtualItem.key} style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: `${virtualItem.size}px`, transform: `translateY(${virtualItem.start}px)`, }} > {items[virtualItem.index]} </div> ))} </div> </div> ); }
9. 服务器组件优化
javascript// 服务器组件默认不发送 JavaScript 到客户端 async function ServerComponent() { const data = await fetchData(); return ( <div> <h1>{data.title}</h1> <p>{data.content}</p> </div> ); } // 只在需要交互的地方使用客户端组件 'use client'; function InteractiveComponent() { const [count, setCount] = useState(0); return <button onClick={() => setCount(c => c + 1)}>{count}</button>; }
10. 使用 Streaming
javascriptimport { Suspense } from 'react'; async function SlowComponent() { const data = await slowFetch(); return <div>{data}</div>; } export default function Page() { return ( <div> <h1>Page Title</h1> <Suspense fallback={<div>Loading...</div>}> <SlowComponent /> </Suspense> </div> ); }
11. 缓存策略
使用 Next.js 缓存
javascript// 缓存 API 响应 export async function getStaticProps() { const data = await fetch('https://api.example.com/data', { cache: 'force-cache', // 或 'no-store', 'no-cache' }).then(r => r.json()); return { props: { data }, revalidate: 3600, // 1 小时 }; }
使用 Redis 缓存
javascriptimport { Redis } from '@upstash/redis'; const redis = new Redis({ url: process.env.UPSTASH_REDIS_REST_URL, token: process.env.UPSTASH_REDIS_REST_TOKEN, }); export async function getCachedData(key) { const cached = await redis.get(key); if (cached) return JSON.parse(cached); const data = await fetchData(); await redis.set(key, JSON.stringify(data), { ex: 3600 }); return data; }
12. 构建优化
分析构建输出
javascript// next.config.js const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer({ // 其他配置 });
压缩和优化
javascript// next.config.js module.exports = { compress: true, swcMinify: true, productionBrowserSourceMaps: false, // 优化图片 images: { formats: ['image/avif', 'image/webp'], deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, };
性能监控
使用 Web Vitals
javascript// pages/_app.js import { useReportWebVitals } from 'next/web-vitals'; export function reportWebVitals(metric) { // 发送到分析服务 console.log(metric); // 或发送到 Google Analytics // gtag('event', metric.name, { value: metric.value }); } export default function App({ Component, pageProps }) { useReportWebVitals(reportWebVitals); return <Component {...pageProps} />; }
最佳实践
- 使用服务器组件:减少客户端 JavaScript
- 优化图片:使用 next/image 组件
- 懒加载:延迟加载非关键资源
- 缓存数据:使用 ISR 和缓存策略
- 监控性能:使用 Web Vitals 监控
- 分析构建:定期分析 bundle 大小
- 使用 CDN:部署到 Vercel 或其他 CDN
- 优化字体:使用 next/font 优化字体加载
通过合理使用这些优化技术,可以显著提升 Next.js 应用的性能和用户体验。