6月2日 23:08

Next.js 性能怎么优化?Server Components、图片和缓存策略实战

Next.js 性能优化从三个方向入手:减少客户端 JavaScript 体积、加快页面加载速度、优化数据获取策略。App Router 的 Server Components 天然比 Pages Router 快——大部分代码不发给浏览器。

1. Server Components 优先

App Router 默认所有组件都是 Server Component。只在需要交互(useState、useEffect、onClick)时才加 'use client'

tsx
// Server Component(默认)— 不发 JS 给浏览器 export default function Page() { return ( <div> <h1>标题</h1> {/* 静态内容 → Server Component */} <SearchBox /> {/* 需要交互 → Client Component */} <ArticleList articles={articles} /> {/* 纯展示 → Server Component */} </div> ); }

常见错误:整个页面都标 'use client'。正确做法是把交互部分抽成小的 Client Component,外层保持 Server Component。

2. 图片优化

next/image 自动做三件事:懒加载、按设备尺寸返回合适分辨率、WebP 格式转换。

tsx
import Image from 'next/image'; <Image src="/hero.jpg" width={1200} height={600} alt="描述" priority // 首屏图片加这个,跳过懒加载 placeholder="blur" // 模糊占位,加载时不会闪白 />

必须填 width 和 height——防止布局偏移(CLS)。priority 只给首屏可见图片用,多了反而拖慢 LCP。

外部图片需要配置域名白名单:

typescript
// next.config.js images: { remotePatterns: [ { protocol: 'https', hostname: 'cdn.example.com' }, ], },

3. 字体优化

next/font 自动内联字体文件,消除 FOUT(字体闪烁):

tsx
import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'] }); export default function Layout({ children }) { return <body className={inter.className}>{children}</body>; }

不要用 CSS @import 加载 Google Fonts——它会阻塞渲染。next/font 在构建时下载字体文件,零网络请求。

4. 动态导入减少首屏 JS

非首屏需要的组件用 dynamic 懒加载:

tsx
import dynamic from 'next/dynamic'; const HeavyChart = dynamic(() => import('./HeavyChart'), { loading: () => <div>加载中...</div>, ssr: false, // 纯客户端组件不需要 SSR });

ssr: false 跳过服务端渲染——适合图表、编辑器这类重交互、不需要 SEO 的组件。

5. 数据获取策略

App Router 的 fetch 默认有缓存:

tsx
// 缓存(默认)— 适合不常变的数据 const data = await fetch('https://api.example.com/data'); // 不缓存 — 每次请求都重新获取 const data = await fetch('https://api.example.com/data', { cache: 'no-store' }); // 定时重新验证 — 适合有一定延迟容忍的数据 const data = await fetch('https://api.example.com/data', { next: { revalidate: 3600 } // 1 小时后重新验证 });

ISR(Incremental Static Regeneration)是 Next.js 的杀手锏:静态页面生成后定时更新,不需要每次请求都渲染。revalidate 的时间根据数据变化频率设置——新闻列表 60 秒,配置数据 3600 秒。

6. Layout 防止重复渲染

App Router 的 layout.tsx 在导航时不会重新渲染。把不会变的 UI(导航栏、页脚)放在 layout 里:

tsx
// app/layout.tsx — 只渲染一次 export default function RootLayout({ children }) { return ( <html> <body> <nav>导航栏</nav> {children} {/* 只有这部分会随路由变化 */} <footer>页脚</footer> </body> </html> ); }

7. 分析打包体积

bash
npx @next/bundle-analyzer

生成可视化报告,找出最大的包。常见问题:整个 lodash 被 import(改用 lodash-es 的按需导入)、moment.js 太大(改用 dayjs)、客户端不必要的包。

性能优化检查清单

  • Server Component 优先,'use client' 只在需要交互时用
  • 图片用 next/image + width/height + priority(首屏)
  • 字体用 next/font,不用 CSS @import
  • 非首屏组件 dynamic 导入
  • fetch 设置合理的 cache/revalidate 策略
  • 静态 UI 放 layout,不放 page
  • bundle-analyzer 检查大包
标签:Next.js