性能优化
本教程共 42 篇 · 第 30 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
30. 性能优化
本节目标:学会用 Core Web Vitals 衡量性能,掌握 Next.js 的预加载、代码分割和包导入优化策略。
Core Web Vitals 基础
Core Web Vitals 是 Google 用来衡量用户体验的核心指标,关注三个方面:
- LCP(Largest Contentful Paint):最大内容绘制,衡量加载速度。目标 ≤ 2.5 秒。
- INP(Interaction to Next Paint):交互到下一次绘制,衡量响应速度。目标 ≤ 200 毫秒。
- CLS(Cumulative Layout Shift):累积布局偏移,衡量视觉稳定性。目标 ≤ 0.1。
这些指标直接影响 SEO 排名和用户体验。Next.js 提供了多种手段来优化它们。
预加载策略
Next.js 的 <Link> 组件默认会在链接进入视口时自动预加载目标页面。这让页面切换感觉瞬间完成。
自动预加载
import Link from 'next/link'
export default function NavLink() {
return <Link href="/about">关于</Link>
}
自动预加载只在生产环境生效。它的工作原理是:先加载目标页面的 React Server Components 数据(RSC Payload),点击时直接使用缓存。
静态路由 vs 动态路由的预加载
| 静态页面 | 动态页面 | |
|---|---|---|
| 预加载内容 | 整个页面 | 只预加载到第一个 loading.js 边界 |
| 客户端缓存 | 5 分钟 | 默认关闭 |
手动预加载
如果需要更精细的控制,可以用 useRouter 的 prefetch 方法:
'use client'
import { useRouter } from 'next/navigation'
export function PricingCard() {
const router = useRouter()
return (
<div onMouseEnter={() => router.prefetch('/pricing')}>
<Link href="/pricing">查看价格</Link>
</div>
)
}
禁用预加载
对于不太可能被点击的链接(比如页脚链接),可以关闭预加载节省带宽:
<Link href="/terms" prefetch={false}>
服务条款
</Link>
预加载调度机制
Next.js 内部维护了一个任务队列,按以下优先级预加载:
- 视口内的链接
- 用户悬停或触摸的链接
- 新链接替换旧链接
- 滚出视口的链接被丢弃
这种机制确保资源用在刀刃上。
代码分割
Next.js 基于路由自动做代码分割。每个路由只加载自己需要的 JavaScript,而不是一整个应用。
动态导入
对于非首屏必需的组件,用 next/dynamic 做懒加载:
import dynamic from 'next/dynamic'
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <p>加载中...</p>,
ssr: false, // 如果组件依赖浏览器 API,可以关闭 SSR
})
export default function Dashboard() {
return <HeavyChart />
}
第三方库的按需导入
很多库(如 lodash、date-fns)导出大量模块,全量导入会增大包体积。Next.js 提供了 optimizePackageImports 来自动优化:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
optimizePackageImports: ['lodash-es', 'date-fns'],
},
}
export default nextConfig
这个选项会自动分析你实际使用了哪些模块,只打包用到的部分。
以下库已经默认优化,不需要手动配置:
lucide-reactdate-fnslodash-esantdrecharts@mui/materialreact-icons/*
图片优化
图片是影响 LCP 的主要因素。next/image 组件自动做以下优化:
- 响应式图片(自动生成
srcset) - 懒加载(默认
loading="lazy") - 现代格式(WebP、AVIF)
- 尺寸优化
import Image from 'next/image'
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="首页大图"
width={1200}
height={600}
preload // 首屏图片加 preload,提前加载(Next.js 16 用 preload 替代已弃用的 priority)
/>
)
}
提示:首屏大图一定要加
preload属性(或使用loading="eager"/fetchPriority="high"),否则可能被懒加载影响 LCP。
字体优化
next/font 自动处理字体文件的加载策略,消除布局偏移(直接影响 CLS):
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }) {
return (
<html className={inter.className}>
<body>{children}</body>
</html>
)
}
预加载的注意事项
避免副作用被意外触发
预加载会执行页面组件的渲染逻辑。如果组件里有副作用(比如统计代码),可能在用户没真正访问时就触发了:
// 错误:预加载时就会执行
export default function Page() {
trackPageView() // 不应该放在组件顶层
return <div>...</div>
}
正确做法是把副作用放到 useEffect 里:
'use client'
export function AnalyticsTracker() {
useEffect(() => {
trackPageView()
}, [])
return null
}
控制预加载数量
渲染大量链接时(比如无限滚动列表),自动预加载可能浪费带宽。可以只在用户悬停时才触发:
'use client'
import Link from 'next/link'
import { useState } from 'react'
export function HoverPrefetchLink({ href, children }) {
const [active, setActive] = useState(false)
return (
<Link
href={href}
prefetch={active ? null : false}
onMouseEnter={() => setActive(true)}
>
{children}
</Link>
)
}
性能测量工具
- Lighthouse:Chrome 内置,给出 Core Web Vitals 评分
- Vercel Analytics:真实用户数据,支持 Web Vitals 监控
- Next.js 内置指标:
next build输出各路由的性能信息
# 本地测试生产环境性能
npm run build
npm run start
性能优化是个持续的过程。先测量,找到瓶颈,再针对性优化。不要过早优化。