首页 / Next.js 16 入门教程 / 性能优化

Next.js 16 入门教程

性能优化

本教程共 42 篇 · 第 30 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程性能优化Core Web Vitals预加载代码分割

30. 性能优化

本节目标:学会用 Core Web Vitals 衡量性能,掌握 Next.js 的预加载、代码分割和包导入优化策略。

Core Web Vitals 基础

Core Web Vitals 是 Google 用来衡量用户体验的核心指标,关注三个方面:

  1. LCP(Largest Contentful Paint):最大内容绘制,衡量加载速度。目标 ≤ 2.5 秒。
  2. INP(Interaction to Next Paint):交互到下一次绘制,衡量响应速度。目标 ≤ 200 毫秒。
  3. 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 分钟默认关闭

手动预加载

如果需要更精细的控制,可以用 useRouterprefetch 方法:

'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 内部维护了一个任务队列,按以下优先级预加载:

  1. 视口内的链接
  2. 用户悬停或触摸的链接
  3. 新链接替换旧链接
  4. 滚出视口的链接被丢弃

这种机制确保资源用在刀刃上。

代码分割

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 />
}

第三方库的按需导入

很多库(如 lodashdate-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-react
  • date-fns
  • lodash-es
  • antd
  • recharts
  • @mui/material
  • react-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>
  )
}

性能测量工具

  1. Lighthouse:Chrome 内置,给出 Core Web Vitals 评分
  2. Vercel Analytics:真实用户数据,支持 Web Vitals 监控
  3. Next.js 内置指标next build 输出各路由的性能信息
# 本地测试生产环境性能
npm run build
npm run start

性能优化是个持续的过程。先测量,找到瓶颈,再针对性优化。不要过早优化。