完整的 Next.js 性能与 SEO 优化指南
Source: Dev.to
内置组件优化
Next.js 提供了经过优化的内置组件,自动遵循性能最佳实践,省去手动优化的步骤,降低常见的陷阱。
next/image
Image 组件是 Next.js 最强大的性能工具之一。它提供:
- 自动懒加载
- 根据设备自动调整图片尺寸
- 格式优化(WebP、AVIF)
- 防止累计布局偏移(CLS)
- 减少带宽消耗
基础实现
import Image from 'next/image';
export default function ProductCard() {
return (
);
}
高级配置(next.config.js)
module.exports = {
images: {
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
domains: ['example.com', 'cdn.example.com'], // 允许的外部域名
remotePatterns: [
{
protocol: 'https',
hostname: '**.example.com',
pathname: '/images/**',
},
],
formats: ['image/webp', 'image/avif'],
minimumCacheTTL: 60,
dangerouslyAllowSVG: true,
contentSecurityPolicy:
"default-src 'self'; script-src 'none'; sandbox;",
},
};
注意要点
- 对首屏图片使用
priority={true}。 - 必须指定
width和height以防止布局偏移。 - 对响应式容器使用
fill属性。 - 外部图片需要在域名白名单中。
- WebP 格式比 JPEG 小约 30 %。
next/link
实现客户端导航,无需完整页面刷新。
import Link from 'next/link';
export default function Navigation() {
return (
About
Blog
);
}
注意要点
- 对可能不会被访问的链接设置
prefetch={false}。
脚本加载策略
优化第三方脚本加载可以避免阻塞主线程,提升页面加载性能。Script 组件提供了对脚本何时以及如何加载的细粒度控制。
关键脚本 – beforeInteractive
在任何 Next.js 代码和页面 hydration 之前加载。用于必须在用户交互前运行的脚本。
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
{children}
);
}
分析与非关键脚本 – afterInteractive
在页面变为可交互后加载。适合分析、广告等非必需脚本。
import Script from 'next/script';
export default function Analytics() {
return (
console.log('Analytics loaded')}
/>
);
}
懒加载脚本 – lazyOnload
在浏览器空闲时间加载。非常适合聊天小部件、社交媒体按钮或其他非关键脚本。
import Script from 'next/script';
export default function ChatWidget() {
return (
console.log('Chat widget ready')}
/>
);
}
Worker 策略 – worker
在 Web Worker 中运行脚本,以获得更好性能。
import Script from 'next/script';
export default function Page() {
return (
);
}
注意要点
- 仅对真正关键的脚本使用
beforeInteractive。 - 大多数分析应使用
afterInteractive。 - 社交媒体小部件和聊天应使用
lazyOnload。 - 优先使用
onLoad/onReady回调,而不是内联脚本。
依赖管理
移除未使用的依赖可以减小 bundle 大小、提升构建速度,并降低安全风险。
识别未使用的包
# 全局安装 depcheck
npm install -g depcheck # 或: yarn global add depcheck
# 在项目根目录运行
depcheck
# 带选项
depcheck --ignores="eslint-*,@types/*"
# 指定目录
depcheck ./src --ignore-dirs=build,dist
示例输出
Unused dependencies
* lodash
* moment
* axios
Missing dependencies
* react-icons
Unused devDependencies
* @testing-library/jest-dom
自动化清理脚本(package.json)
{
"scripts": {
"deps:check": "depcheck",
"deps:clean": "depcheck --json | jq -r '.dependencies[]' | xargs npm uninstall"
}
}
注意要点
- 定期(如每月或大版本发布前)运行
depcheck。 - 在移除之前确认只在配置文件中使用的包。
- 类型声明包(
@types/*)可能不会出现在搜索结果中,如有需要请保留。 - 移除依赖后务必彻底测试。
- 将
devDependencies与dependencies分开管理。 - 记录为何保留看似未使用的包。
缓存与增量静态再生成(ISR)
智能缓存策略可以更快地提供内容,降低服务器负载,并保持数据新鲜。ISR 允许在构建后更新静态页面。
基础 ISR 实现
// app/blog/[slug]/page.js
export const revalidate = 3600; // 每小时重新验证
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map(post => ({ slug: post.slug }));
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return (
## {post.title}
{post.content}
{post.publishedAt}
);
}
按需重新验证
// app/api/revalidate/route.js
import { revalidatePath } from 'next/cache';
import { NextResponse } from 'next/server';
export async function POST(request) {
const secret = request.nextUrl.searchParams.get('secret');
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ message: 'Invalid secret' }, { status: 401 });
}
const path = request.nextUrl.searchParams.get('path');
if (path) {
revalidatePath(path);
return NextResponse.json({ revalidated: true, now: Date.now() });
}
return NextResponse.json({ message: 'Missing path' }, { status: 400 });
}
基于标签的重新验证
// Fetch with cache tags
export default async function ProductList() {
const products = await fetch('https://api.example.com/products', {
next: { tags: ['products'], revalidate: 3600 }
});
return {/* Render products */};
}
// app/api/revalidate-products/route.js
import { revalidateTag } from 'next/cache';
import { NextResponse } from 'next/server';
export async function POST() {
revalidateTag('products');
return NextResponse.json({ revalidated: true });
}
Cache‑Control 响应头
// app/api/data/route.js
export async function GET() {
const data = await fetchData();
return NextResponse.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300'
}
});
}
注意要点
- ISR 适用于定期更新的内容(博客、商品列表)。
- 对频繁变化的数据使用更短的重新验证间隔(例如 60 秒)。
- 将 ISR 与基于标签的重新验证结合使用,可实现细粒度缓存失效。
字体优化
使用内置的 next/font API 时,Next.js 会自动优化字体。
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
weight: ['400', '700'],
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
{children}
);
}
技巧
- 只加载所需的字符子集。
- 优先使用可变字体,以减少请求次数。
- 避免在 CSS 中使用
@import,改用next/fontAPI。
懒加载与代码拆分
动态导入
import dynamic from 'next/dynamic';
const HeavyComponent = dynamic(() => import('../components/HeavyComponent'), {
loading: () =>
Loading…
,
ssr: false, // 仅在客户端加载
});
- 将大型库(如图表、地图)拆分为独立块。
- 对依赖浏览器 API 的组件使用
ssr: false。
列表虚拟化
对于长列表,使用 react-window 或 react-virtualized 等库,仅渲染可见项。
import { FixedSizeList as List } from 'react-window';
export default function VirtualizedList({ items }) {
return (
{({ index, style }) => (
{items[index].title}
)}
);
}
数据获取优化
客户端使用 SWR
import useSWR from 'swr';
const fetcher = url => fetch(url).then(res => res.json());
export default function Profile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher, {
revalidateOnFocus: false,
dedupingInterval: 60000,
});
if (isLoading) return
Loading…
;
if (error) return
Error loading profile.
;
return {data.name};
}
- 对不需要频繁更新的数据将
revalidateOnFocus设为false。 - 调整
dedupingInterval以控制请求去重频率。
JavaScript 包体优化
- Tree‑shaking:只导入需要的函数(
import { foo } from 'lodash'而不是import _ from 'lodash')。 - 分析包体:使用
next build && next analyze或webpack-bundle-analyzer等工具。 - 压缩资源:在 CDN 或 Vercel Edge 上启用 gzip/Brotli。
CSS 优化
- 使用 CSS 模块或 styled‑components 对样式进行作用域化,避免全局膨胀。
- 使用
purgecss(在 Next.js 中通过next-purgecss集成)移除未使用的 CSS。 - 对响应式设计优先使用
@media查询,而不是拆分多个 CSS 文件。
API 路由缓存
在 Edge 端缓存耗时的 API 响应。
// app/api/products/route.js
import { NextResponse } from 'next/server';
import { fetchProducts } from '@/lib/db';
export async function GET() {
const products = await fetchProducts();
return NextResponse.json(products, {
headers: {
'Cache-Control': 'public, s-maxage=120, stale-while-revalidate=300',
},
});
}
Edge Runtime
将计算密集型逻辑部署到 Edge Runtime,以获得低延迟。
// app/api/geo/route.js
export const runtime = 'edge';
export async function GET(request) {
const ip = request.headers.get('x-forwarded-for') ?? 'unknown';
// 快速进行地理位置查询
return new Response(`Your IP: ${ip}`);
}
核心网络指标(Core Web Vitals)
- Largest Contentful Paint (LCP):通过优化首屏内容保持 LCP 较低。
export default function BlogPost({ post }) {
return (
{post.title} – My Blog
{/* Post content */}
);
}
安全优化
- 在 API 路由或中间件中设置
Content‑Security‑Policy、X‑Content‑Type‑Options、Referrer‑Policy等响应头。 - 使用
next-safe或类似库对用户生成的 HTML 进行消毒。 - 保持依赖最新;定期运行
npm audit。
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; img-src https: data:; script-src 'self' 'unsafe-inline';"
);
return response;
}