关于 nextjs
nextjs 技能提供面向生产环境的 Next.js App Router 指引,重点涵盖 Next.js 16 变更和迁移问题。适合开发或升级 App Router 应用的开发者,并附有示例、模板和故障排查资料。
主要功能
- 01说明 Next.js 16 重大变更
- 02指导从 Next.js 15 迁移
- 03介绍 Cache Components 与缓存 API
- 04记录路由处理器和 proxy 变更
- 05提供模板和错误解决方案
- 06汇总安全公告与版本要求
适用场景
- 01将 App Router 项目迁移到 Next.js 16
- 02更新异步路由 API 的代码
- 03配置缓存和并行路由
- 04排查文档涵盖的 Next.js 错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill nextjs安装后在 Claude Code 中输入/nextjs确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.md.claude-plugin/plugin.jsoncommands/init.mdreferences/next-16-migration-guide.mdtop-errors.mdrules/nextjs.mdscripts/check-versions.shtemplates/app-router-async-params.tsxcache-component-use-cache.tsxpackage.jsonparallel-routes-with-default.tsxproxy-migration.tsroute-handler-api.tsserver-actions-form.tsx详细介绍
Next.js App Router - 生产模式
版本:Next.js 16.1.1 React 版本:19.2.3 Node.js:20.9+ 最后验证时间:2026-01-09
目录
- 何时使用此技能
- 何时不使用此技能
- 安全公告(2025 年 12 月)
- Next.js 16.1 更新
- Next.js 16 重大变更
- 缓存组件与缓存 API
- 路由处理程序(Next.js 16 更新)
- Proxy 与 Middleware
- 并行路由 - 必须提供 default.js
- React 19.2 功能
- Turbopack(在 Next.js 16 中稳定)
- 常见错误与解决方案
- 模板与资源
何时使用此技能
重点:Next.js 16 的重大变更与知识盲区(2024 年 12 月起)。
在以下情况下使用此技能:
- Next.js 16 重大变更(异步 params、proxy.ts、并行路由 default.js、已移除的功能)
- 使用
"use cache"指令的 缓存组件(Next.js 16 中新增) - 新的缓存 API:
revalidateTag()、updateTag()、refresh()(在 Next.js 16 中更新) - 从 Next.js 15 迁移到 16(避免重大变更错误)
- 异步路由参数(
params、searchParams、cookies()、headers()现在均为异步) - 带 default.js 的并行路由(Next.js 16 中必需)
- React 19.2 功能(视图过渡、
useEffectEvent()、React Compiler) - Turbopack(在 Next.js 16 中已稳定并设为默认)
- 图像默认值已更改(Next.js 16 中的 TTL、尺寸和质量)
- 错误预防(记录了 25 个 Next.js 16 错误及其解决方案)
何时不使用此技能
以下情况请勿使用此技能:
- 部署到 Cloudflare Workers → 请改用
cloudflare-nextjs技能 - Pages Router 模式 → 此技能仅涵盖 App Router(Pages Router 已过时)
- 身份验证库 → 请使用
clerk-auth、better-auth或其他身份验证专用技能 - 数据库集成 → 请使用
cloudflare-d1、drizzle-orm-d1或数据库专用技能 - UI 组件库 → Tailwind + shadcn/ui 请使用
tailwind-v4-shadcn技能 - 状态管理 → 请使用
zustand-state-management、tanstack-query技能 - 表单库 → 请使用
react-hook-form-zod技能 - Vercel 专属功能 → 请参阅 Vercel 平台文档
- Next.js Enterprise 功能(ISR、DPR)→ 请参阅 Next.js Enterprise 文档
- 部署配置 → 请使用平台专用的部署技能
与其他技能的关系:
- cloudflare-nextjs:用于将 Next.js 部署到 Cloudflare Workers(若部署到 Cloudflare,请同时使用这两项技能)
- tailwind-v4-shadcn:用于配置 Tailwind v4 + shadcn/ui(可与此技能搭配使用)
- clerk-auth:用于在 Next.js 中使用 Clerk 进行身份验证(可与此技能搭配使用)
- better-auth:用于集成 Better Auth(可与此技能搭配使用)
安全公告(2025 年 12 月)
严重:2025 年 12 月披露了三个影响使用 React Server Components 的 Next.js 版本的安全漏洞:
| CVE | 严重程度 | 受影响版本 | 描述 |
|---|---|---|---|
| CVE-2025-66478 | 严重(10.0) | 15.x、16.x | Server Component 任意代码执行 |
| CVE-2025-55184 | 高 | 13.x-16.x | 通过格式错误的请求造成拒绝服务 |
| CVE-2025-55183 | 中 | 13.x-16.x | 错误响应中暴露源代码 |
必须采取的措施:立即升级到 Next.js 16.1.1 或更高版本。
npm update next
# Verify: npm list next should show 16.1.1+
参考资料:
Next.js 16.1 更新(2025 年 12 月)
16.1 新增功能:
- Turbopack 文件系统缓存(稳定版):现已在开发环境中默认启用
- Next.js Bundle Analyzer:用于分析捆绑包的新实验性功能
- 调试功能改进:增强了对
next dev --inspect的支持 - 安全修复:修复了 CVE-2025-66478、CVE-2025-55184、CVE-2025-55183
Next.js 16 重大变更
重要:Next.js 16 引入了多项重大变更。如果要从 Next.js 15 或更早版本迁移,请仔细阅读本节。
1. 异步路由参数(重大变更)
重大变更:params、searchParams、cookies()、headers()、draftMode() 现在都是异步的,必须使用 await。
之前(Next.js 15):
// ❌ This no longer works in Next.js 16
export default function Page({ params, searchParams }: {
params: { slug: string }
searchParams: { query: string }
}) {
const slug = params.slug // ❌ Error: params is a Promise
const query = searchParams.query // ❌ Error: searchParams is a Promise
return <div>{slug}</div>
}
之后(Next.js 16):
// ✅ Correct: await params and searchParams
export default async function Page({ params, searchParams }: {
params: Promise<{ slug: string }>
searchParams: Promise<{ query: string }>
}) {
const { slug } = await params // ✅ Await the promise
const { query } = await searchParams // ✅ Await the promise
return <div>{slug}</div>
}
适用范围:
- 页面、布局、路由处理程序中的
params - 页面中的
searchParams - 来自
next/headers的cookies() - 来自
next/headers的headers() - 来自
next/headers的draftMode()
迁移:
// ❌ Before
import { cookies, headers } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies() // ❌ Sync access
const headersList = headers() // ❌ Sync access
}
// ✅ After
import { cookies, headers } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies() // ✅ Async access
const headersList = await headers() // ✅ Async access
}
Codemod:运行 npx @next/codemod@canary upgrade latest 可自动完成迁移。
Codemod 的局限性(来自社区): 官方 codemod 可处理约 80% 的异步 API 迁移,但无法处理一些边缘情况:
- 在自定义钩子中访问异步 API
- 通过条件逻辑访问 params
- 从外部包导入组件
- 包含多个异步调用的复杂服务器操作
运行 codemod 后,请搜索 @next-codemod-error 注释,查找无法自动修复的位置。
客户端组件的手动迁移:
// For client components, use React.use() to unwrap promises
'use client';
import { use } from 'react';
export default function ClientComponent({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = use(params); // Unwrap Promise in client
return <div>{id}</div>;
}
参见模板:templates/app-router-async-params.tsx
2. Middleware → Proxy 迁移(重大变更)
重大变更:Next.js 16 中的 middleware.ts 已弃用。请改用 proxy.ts。
变更原因:proxy.ts 在 Node.js 运行时(而非 Edge 运行时)运行,从而明确标示网络边界。这能更清晰地区分边缘中间件与服务器端代理。
迁移步骤:
- 重命名文件:
middleware.ts→proxy.ts - 重命名函数:
middleware→proxy - 更新配置:
matcher→config.matcher(语法不变)
之前(Next.js 15):
// middleware.ts ❌ Deprecated in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*',
}
之后(Next.js 16):
// proxy.ts ✅ New in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*',
}
注意:middleware.ts 在 Next.js 16 中仍可使用,但已弃用。请迁移到 proxy.ts,以确保未来的兼容性。
参见模板:templates/proxy-migration.ts
参见参考文档:references/proxy-vs-middleware.md
3. 并行路由必须包含 default.js(重大变更)
重大变更:并行路由现在必须包含显式的 default.js 文件。缺少该文件时,软导航期间路由会失败。
结构:
app/
├── @auth/
│ ├── login/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
├── @dashboard/
│ ├── overview/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
└── layout.tsx
布局:
// app/layout.tsx
export default function Layout({
children,
auth,
dashboard,
}: {
children: React.ReactNode
auth: React.ReactNode
dashboard: React.ReactNode
}) {
return (
<html>
<body>
{auth}
{dashboard}
{children}
</body>
</html>
)
}
默认回退(必需):
// app/@auth/default.tsx
export default function AuthDefault() {
return null // or <Skeleton /> or redirect
}
// app/@dashboard/default.tsx
export default function DashboardDefault() {
return null
}
必须包含的原因:Next.js 16 更改了并行路由处理软导航的方式。缺少 default.js 时,客户端导航期间未匹配的插槽会报错。
参见模板:templates/parallel-routes-with-default.tsx
4. 已移除的功能(重大变更)
以下功能已在 Next.js 16 中移除:
- AMP 支持 —— 已完全移除。请迁移到标准页面。
next lint命令 —— 请直接使用 ESLint 或 Biome。serverRuntimeConfig和publicRuntimeConfig—— 请改用环境变量。experimental.ppr标志 —— 已发展为 Cache Components。请使用"use cache"指令。- 自动设置
scroll-behavior: smooth—— 如有需要,请手动添加。 - Node.js 18 支持 —— 最低版本现为 20.9+。
迁移:
- AMP:将 AMP 页面转换为标准页面,或使用单独的 AMP 实现。
- Lint:直接运行
npx eslint .或npx biome lint .。 - 配置:将
serverRuntimeConfig替换为process.env.VARIABLE。 - PPR:从
experimental.ppr迁移到"use cache"指令(参见 Cache Components 部分)。
5. 版本要求(不兼容变更)
Next.js 16 要求:
- Node.js:20.9+(不再支持 Node.js 18)
- TypeScript:5.1+(如果使用 TypeScript)
- React:19.2+(随 Next.js 16 自动安装)
- 浏览器:Chrome 111+、Safari 16.4+、Firefox 109+、Edge 111+
检查版本:
node --version # Should be 20.9+
npm --version # Should be 10+
npx next --version # Should be 16.0.0+
升级 Node.js:
# Using nvm
nvm install 20
nvm use 20
nvm alias default 20
# Using Homebrew (macOS)
brew install node@20
# Using apt (Ubuntu/Debian)
sudo apt update
sudo apt install nodejs npm
6. 图片默认值变更(不兼容变更)
Next.js 16 更改了 next/image 的默认值:
| 设置 | Next.js 15 | Next.js 16 |
|---|---|---|
| TTL(缓存时长) | 60 秒 | 4 小时 |
| imageSizes | [16, 32, 48, 64, 96, 128, 256, 384] | [640, 750, 828, 1080, 1200](减少) |
| qualities | [75, 90, 100] | [75](单一画质) |
影响:
- 图片缓存时间更长(4 小时,而非 60 秒)
- 生成的图片尺寸更少(构建体积更小,但粒度更粗)
- 生成单一画质(75),而非多种画质
覆盖默认值(如有需要):
// next.config.ts
import type { NextConfig } from 'next'
const config: NextConfig = {
images: {
minimumCacheTTL: 60, // Revert to 60 seconds
deviceSizes: [640, 750, 828, 1080, 1200, 1920], // Add larger sizes
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // Restore old sizes
formats: ['image/webp'], // Default
},
}
export default config
参见模板:templates/image-optimization.tsx
缓存组件与缓存 API
Next.js 16 新增:缓存组件通过 "use cache" 指令引入选择性缓存,取代 Next.js 15 中的隐式缓存。
1. 概述
变更内容:
- Next.js 15:隐式缓存(默认缓存所有服务器组件)
- Next.js 16:使用
"use cache"指令进行选择性缓存
变更原因:显式缓存让开发者拥有更多控制权,并使缓存行为更可预测。
重要的默认缓存行为(来源于社区):
| 功能 | Next.js 14 | Next.js 15/16 |
|---|---|---|
| fetch() 请求 | 默认缓存 | 默认不缓存 |
| 路由器缓存(动态页面) | 在客户端缓存 | 默认不缓存 |
| 路由器缓存(静态页面) | 缓存 | 仍然缓存 |
| 路由处理程序(GET) | 缓存 | 默认动态 |
最佳实践:在 Next.js 16 中默认采用动态渲染。先不启用缓存,再在有益的地方添加缓存,而不是排查意外的缓存命中。务必使用生产构建进行测试——开发服务器的行为有所不同。
缓存组件支持:
- 组件级缓存(缓存特定组件,而非整个页面)
- 函数级缓存(缓存开销较大的计算)
- 页面级缓存(有选择地缓存整个页面)
- 部分预渲染(PPR)——缓存静态部分,按需渲染动态部分
2. "use cache" 指令
语法:在服务器组件、函数或路由处理程序的顶部添加 "use cache"。
组件级缓存:
// app/components/expensive-component.tsx
'use cache'
export async function ExpensiveComponent() {
const data = await fetch('https://api.example.com/data')
const json = await data.json()
return (
<div>
<h1>{json.title}</h1>
<p>{json.description}</p>
</div>
)
}
函数级缓存:
// lib/data.ts
'use cache'
export async function getExpensiveData(id: string) {
const response = await fetch(`https://api.example.com/items/${id}`)
return response.json()
}
// Usage in component
import { getExpensiveData } from '@/lib/data'
export async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const product = await getExpensiveData(id) // Cached
return <div>{product.name}</div>
}
页面级缓存:
// app/blog/[slug]/page.tsx
'use cache'
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}
参见模板:templates/cache-component-use-cache.tsx
3. 部分预渲染(PPR)
PPR 允许缓存页面的静态部分,同时按需渲染动态部分。
模式:
// app/dashboard/page.tsx
// Static header (cached)
'use cache'
async function StaticHeader() {
return <header>My App</header>
}
// Dynamic user info (not cached)
async function DynamicUserInfo() {
const cookieStore = await cookies()
const userId = cookieStore.get('userId')?.value
const user = await fetch(`/api/users/${userId}`).then(r => r.json())
return <div>Welcome, {user.name}</div>
}
// Page combines both
export default function Dashboard() {
return (
<div>
<StaticHeader /> {/* Cached */}
<DynamicUserInfo /> {/* Dynamic */}
</div>
)
}
适用场景:
- 页面同时包含静态和动态内容
- 需要缓存布局、页眉和页脚,同时渲染用户专属内容
- 需要快速初始加载(静态部分)和个性化内容(动态部分)
参见参考资料:references/cache-components-guide.md
4. revalidateTag()——更新后的 API
不兼容变更:revalidateTag() 现在需要第二个参数(cacheLife 配置),以实现 stale-while-revalidate 行为。
之前(Next.js 15):
import { revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await fetch(`/api/posts/${id}`, { method: 'PATCH' })
revalidateTag('posts') // ❌ Only one argument in Next.js 15
}
之后(Next.js 16):
import { revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await fetch(`/api/posts/${id}`, { method: 'PATCH' })
revalidateTag('posts', 'max') // ✅ Second argument required in Next.js 16
}
内置缓存生命周期配置:
'max'——最大陈旧时间(大多数用例推荐)'hours'——数小时后变为陈旧'days'——数天后变为陈旧'weeks'——数周后变为陈旧'default'——默认缓存行为
自定义缓存生命周期配置:
revalidateTag('posts', {
stale: 3600, // Stale after 1 hour (seconds)
revalidate: 86400, // Revalidate every 24 hours (seconds)
expire: false, // Never expire (optional)
})
服务器操作中的模式:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify({ title, content }),
})
revalidateTag('posts', 'max') // ✅ Revalidate with max staleness
}
参见模板:templates/revalidate-tag-cache-life.ts
5. updateTag() - 新 API(仅限 Server Actions)
Next.js 16 新增:updateTag() 为 Server Actions 提供读己之写语义。
功能:
- 立即使缓存过期
- 在同一请求中刷新数据
- 突变后立即显示更新后的数据(无陈旧数据)
与 revalidateTag() 的区别:
revalidateTag():过期时重新验证(显示陈旧数据,并在后台重新验证)updateTag():立即刷新(使缓存过期,并在同一请求中获取新数据)
使用场景:表单、用户设置,或用户期望立即获得反馈的任何突变操作。
模式:
'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(formData: FormData) {
const name = formData.get('name') as string
const email = formData.get('email') as string
// Update database
await db.users.update({ name, email })
// Immediately refresh cache (read-your-writes)
updateTag('user-profile')
// User sees updated data immediately (no stale data)
}
使用时机:
updateTag():用户设置、个人资料更新、关键突变操作(立即反馈)revalidateTag():博客文章、产品列表、非关键更新(后台重新验证)
参见模板:templates/server-action-update-tag.ts
6. refresh() - 新 API(仅限 Server Actions)
Next.js 16 新增:refresh() 只刷新未缓存的数据(与客户端的 router.refresh() 互为补充)。
使用时机:
- 刷新动态数据,而不影响已缓存的数据
- 在服务端配合使用
router.refresh()
模式:
'use server'
import { refresh } from 'next/cache'
export async function refreshDashboard() {
// Refresh uncached data (e.g., real-time metrics)
refresh()
// Cached data (e.g., static header) remains cached
}
与 revalidateTag() 和 updateTag() 的区别:
refresh():只刷新未缓存的数据revalidateTag():重新验证带有特定标签的数据(过期时重新验证)updateTag():立即使带有特定标签的数据过期并刷新
参见参考文档:references/cache-components-guide.md
路由处理器(Next.js 16 更新)
路由处理器中的异步参数(重大变更)
重要:在 Next.js 16 路由处理器中,params 和 headers() 现在是异步的。
示例:
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'
import { headers } from 'next/headers'
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params // ✅ Await params in Next.js 16
const headersList = await headers() // ✅ Await headers in Next.js 16
const post = await db.posts.findUnique({ where: { id } })
return NextResponse.json(post)
}
参见模板:templates/route-handler-api.ts
Proxy 与 Middleware
Next.js 16 引入了 proxy.ts,用于替代 middleware.ts。
为什么要更改?
middleware.ts:在 Edge 运行时运行(Node.js API 受限)proxy.ts:在 Node.js 运行时运行(可使用完整的 Node.js API)
新的 proxy.ts 明确了网络边界,并提供了更大的灵活性。
迁移
之前(middleware.ts):
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Check auth
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: '/dashboard/:path*',
}
之后(proxy.ts):
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Check auth
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: '/dashboard/:path*',
}
参见模板:templates/proxy-migration.ts
参见参考文档:references/proxy-vs-middleware.md
并行路由 - 必须提供 default.js(重大变更)
Next.js 16 中的重大变更:并行路由现在必须显式提供 default.js 文件。
结构:
app/
├── @modal/
│ ├── login/page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
├── @feed/
│ ├── trending/page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
└── layout.tsx
默认文件(必需):
// app/@modal/default.tsx
export default function ModalDefault() {
return null // or <Skeleton /> or redirect
}
必须提供的原因:Next.js 16 更改了软导航的处理方式。如果没有 default.js,客户端导航期间未匹配的插槽会报错。
高级边缘情况(来自社区):
即使有 default.js 文件,对含有并行路由的路由执行硬导航或刷新,也可能返回 404 错误。解决方法是添加一个捕获所有路由。
解决方法:
// app/@modal/[...catchAll]/page.tsx
export default function CatchAll() {
return null;
}
// OR use catch-all in default.tsx
// app/@modal/default.tsx
export default function ModalDefault({ params }: { params: { catchAll?: string[] } }) {
return null; // Handles all unmatched routes
}
参见模板:templates/parallel-routes-with-default.tsx
React 19.2 功能
Next.js 16 集成了 React 19.2,其中包含来自 React Canary 的新功能。
1. 视图过渡
使用场景:在页面切换之间实现平滑动画。
'use client'
import { useRouter } from 'next/navigation'
import { startTransition } from 'react'
export function NavigationLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter()
function handleClick(e: React.MouseEvent) {
e.preventDefault()
// Wrap navigation in startTransition for View Transitions
startTransition(() => {
router.push(href)
})
}
return <a href={href} onClick={handleClick}>{children}</a>
}
配合 CSS View Transitions API 使用:
/* app/globals.css */
@view-transition {
navigation: auto;
}
/* Animate elements with view-transition-name */
.page-title {
view-transition-name: page-title;
}
参见模板:templates/view-transitions-react-19.tsx
2. useEffectEvent()(实验性)
使用场景:从 useEffect 中提取非响应式逻辑。
'use client'
import { useEffect, experimental_useEffectEvent as useEffectEvent } from 'react'
export function ChatRoom({ roomId }: { roomId: string }) {
const onConnected = useEffectEvent(() => {
console.log('Connected to room:', roomId)
})
useEffect(() => {
const connection = connectToRoom(roomId)
onConnected() // Non-reactive callback
return () => connection.disconnect()
}, [roomId]) // Only re-run when roomId changes
return <div>Chat Room {roomId}</div>
}
使用原因:当回调依赖项发生变化时,可避免不必要地重新运行 useEffect。
3. React Compiler(稳定版)
使用场景:无需使用 useMemo、useCallback 即可自动记忆化。
在 next.config.ts 中启用:
import type { NextConfig } from 'next'
const config: NextConfig = {
experimental: {
reactCompiler: true,
},
}
export default config
安装插件:
npm install babel-plugin-react-compiler
示例(无需手动记忆化):
'use client'
export function ExpensiveList({ items }: { items: string[] }) {
// React Compiler automatically memoizes this
const filteredItems = items.filter(item => item.length > 3)
return (
<ul>
{filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
)
}
参见参考文档:references/react-19-integration.md
Turbopack(在 Next.js 16 中已稳定)
新增:Turbopack 现已成为 Next.js 16 中的默认打包器。
性能提升:
- 生产构建速度提升 2–5 倍
- Fast Refresh 速度最高提升 10 倍
退出使用(如有需要):
npm run build -- --webpack
启用文件系统缓存(实验性):
// next.config.ts
import type { NextConfig } from 'next'
const config: NextConfig = {
experimental: {
turbopack: {
fileSystemCaching: true, // Beta: Persist cache between runs
},
},
}
export default config
Turbopack 的生产环境限制(截至 Next.js 16.1)
已知问题:
1. 与 Prisma 不兼容
使用 Prisma ORM(v6.5.0+)时,Turbopack 生产构建会失败。错误:“'path' 参数必须是字符串类型。”
解决方法:
# Use webpack for production builds
npm run build -- --webpack
或在 next.config.ts 中:
const config: NextConfig = {
experimental: {
turbo: false, // Disable Turbopack for production
},
};
2. Source Maps 安全风险
Turbopack 目前始终会为浏览器构建生产环境 Source Maps,导致生产部署中源代码暴露。
解决方法:
// next.config.ts
const config: NextConfig = {
productionBrowserSourceMaps: false, // Disable source maps
};
或在部署时排除 .map 文件:
# .vercelignore or similar
*.map
3. 外部模块哈希不匹配(Monorepo)
当 node_modules 结构不同时(pnpm、yarn workspaces、monorepo),Turbopack 生成的外部模块引用哈希会不匹配。这会导致生产构建中出现“找不到模块”错误。
症状:
- 本地构建成功,但在 CI/CD 中失败
- 打包后的引用与实际模块文件之间存在哈希不匹配
解决方法:
// next.config.ts
const config: NextConfig = {
experimental: {
serverExternalPackages: ['package-name'], // Explicitly externalize packages
},
};
4. 打包体积差异(来自社区)
使用 Turbopack 构建的打包体积可能与使用 webpack 构建的不同。这是预期情况,Turbopack 正在不断成熟和优化。
常见错误与解决方法
1. 错误:params 是一个 Promise
错误:
Type 'Promise<{ id: string }>' is not assignable to type '{ id: string }'
原因:Next.js 16 将 params 改为异步。
解决方法:等待 params:
// ❌ Before
export default function Page({ params }: { params: { id: string } }) {
const id = params.id
}
// ✅ After
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
}
2. 错误:searchParams 是一个 Promise
错误:
Property 'query' does not exist on type 'Promise<{ query: string }>'
原因:在 Next.js 16 中,searchParams 现在是异步的。
解决方法:
// ❌ Before
export default function Page({ searchParams }: { searchParams: { query: string } }) {
const query = searchParams.query
}
// ✅ After
export default async function Page({ searchParams }: { searchParams: Promise<{ query: string }> }) {
const { query } = await searchParams
}
3. 错误:cookies() 需要使用 await
错误:
'cookies' implicitly has return type 'any'
原因:在 Next.js 16 中,cookies() 现在是异步的。
解决方法:
// ❌ Before
import { cookies } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies()
}
// ✅ After
import { cookies } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies()
}
4. 错误:并行路由缺少 default.js
错误:
Error: Parallel route @modal/login was matched but no default.js was found
原因:Next.js 16 要求所有并行路由都包含 default.js。
解决方法:添加 default.tsx 文件:
// app/@modal/default.tsx
export default function ModalDefault() {
return null
}
5. 错误:revalidateTag() 需要 2 个参数
错误:
Expected 2 arguments, but got 1
原因:在 Next.js 16 中,revalidateTag() 现在需要一个 cacheLife 参数。
解决方法:
// ❌ Before
revalidateTag('posts')
// ✅ After
revalidateTag('posts', 'max')
6. 错误:无法在 Server Component 中使用 React Hooks
错误:
You're importing a component that needs useState. It only works in a Client Component
原因:在 Server Component 中使用了 React Hooks。
解决方法:添加 'use client' 指令:
// ✅ Add 'use client' at the top
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
7. 错误:middleware.ts 已弃用
警告:
Warning: middleware.ts is deprecated. Use proxy.ts instead.
解决方法:迁移到 proxy.ts:
// Rename: middleware.ts → proxy.ts
// Rename function: middleware → proxy
export function proxy(request: NextRequest) {
// Same logic
}
8. 错误:Turbopack 构建失败
错误:
Error: Failed to compile with Turbopack
原因:Turbopack 现已成为 Next.js 16 的默认打包器。
解决方法:如果不兼容,请退出使用 Turbopack:
npm run build -- --webpack
9. 错误:next/image 的 src 无效
错误:
Invalid src prop (https://example.com/image.jpg) on `next/image`. Hostname "example.com" is not configured under images in your `next.config.js`
解决方法:在 next.config.ts 中添加远程模式:
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
}
10. 错误:无法将 Server Component 导入 Client Component
错误:
You're importing a Server Component into a Client Component
解决方法:将 Server Component 作为子项传入:
// ❌ Wrong
'use client'
import { ServerComponent } from './server-component' // Error
export function ClientComponent() {
return <ServerComponent />
}
// ✅ Correct
'use client'
export function ClientComponent({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
// Usage
<ClientComponent>
<ServerComponent /> {/* Pass as children */}
</ClientComponent>
11. 错误:generateStaticParams 不起作用
原因:generateStaticParams 仅适用于静态生成(export const dynamic = 'force-static')。
解决方法:
export const dynamic = 'force-static'
export async function generateStaticParams() {
const posts = await fetch('/api/posts').then(r => r.json())
return posts.map((post: { id: string }) => ({ id: post.id }))
}
12. 错误:fetch() 未缓存
原因:Next.js 16 使用通过 "use cache" 指令选择性启用的缓存。
解决方法:在组件或函数中添加 "use cache":
'use cache'
export async function getPosts() {
const response = await fetch('/api/posts')
return response.json()
}
13. 错误:路由组发生路由冲突
错误:
Error: Conflicting routes: /about and /(marketing)/about
原因:路由组生成了相同的 URL 路径。
解决方案:确保路由组之间没有冲突:
app/
├── (marketing)/about/page.tsx → /about
└── (shop)/about/page.tsx → ERROR: Duplicate /about
# Fix: Use different routes
app/
├── (marketing)/about/page.tsx → /about
└── (shop)/store-info/page.tsx → /store-info
14. 错误:元数据未更新
原因:使用动态元数据,却没有使用 generateMetadata()。
解决方案:对动态页面使用 generateMetadata():
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
const post = await fetch(`/api/posts/${id}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
}
}
15. 错误:next/font 字体未加载
原因:字体变量未应用到 HTML 元素。
解决方案:将字体变量应用到 <html> 或 <body>:
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={inter.variable}> {/* ✅ Apply variable */}
<body>{children}</body>
</html>
)
}
16. 错误:浏览器中无法使用环境变量
原因:仅限服务端使用的环境变量不会暴露给浏览器。
解决方案:添加前缀 NEXT_PUBLIC_,以便在客户端访问:
# .env
SECRET_KEY=abc123 # Server-only
NEXT_PUBLIC_API_URL=https://api # Available in browser
// Server Component (both work)
const secret = process.env.SECRET_KEY
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// Client Component (only public vars work)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
17. 错误:未找到 Server Action
错误:
Error: Could not find Server Action
原因:缺少 'use server' 指令。
解决方案:添加 'use server':
// ❌ Before
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
// ✅ After
'use server'
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
18. 错误:TypeScript 路径别名不起作用
原因:tsconfig.json 中的 baseUrl 或 paths 配置不正确。
解决方案:正确配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@/components/*": ["./app/components/*"]
}
}
}
参见参考资料:references/top-errors.md
19. 错误:多次重定向导致客户端导航受限
错误:Throttling navigation to prevent the browser from hanging
来源:GitHub Issue #87245
原因:当 proxy.ts(或 middleware.ts)执行重定向以添加查询参数,并且 Server Component 也调用 redirect() 添加其他查询参数时,在生产构建中,通过 <Link> 进行客户端导航会失败。这是从 Next.js 14 到 16 的回归问题。
症状:
- 在
next dev(开发模式)下正常 - 直接访问 URL(完整页面加载)时正常
- 在生产构建中通过
<Link>进行客户端导航时失败 - 预取会导致无限重定向循环
解决方案:对导航到包含重定向逻辑页面的链接禁用预取:
// ✅ Workaround: Disable prefetch
<Link href="/my-route" prefetch={false}>
Navigate
</Link>
20. 错误:Cache Components 与 i18n 动态片段不兼容
错误:即使使用了 generateStaticParams,路由仍会变为动态路由
来源:GitHub Issue #86870
原因:使用 intlayer、next-intl 或 lingui 等国际化(i18n)框架时,Cache Components("use cache" 指令)无法用于动态片段。即使在布局级别使用了 generateStaticParams,访问 params 也会使路由变为动态路由。
原因说明:每个 i18n 框架都需要访问 params 来获取区域设置。在 Next.js 16 中,访问 params 是异步调用,这会使整个页面退出缓存。
解决方案:在每个动态片段级别都添加 generateStaticParams:
// app/[locale]/[id]/page.tsx
export async function generateStaticParams() {
return [
{ locale: 'en', id: '1' },
{ locale: 'en', id: '2' },
// ... all combinations
];
}
'use cache'
export default async function Page({ params }: Props) {
// Now caching works
}
补充说明:[locale] 动态片段在编译期间会收到 _next 等无效值,在初始化 i18n 提供程序时导致 RangeError: Incorrect locale information provided。
21. 错误:在 Server Components 中,instanceof 对自定义错误类失效
错误:即使错误属于 CustomError,instanceof CustomError 也会返回 false
来源:GitHub Issue #87614
原因:Server Components 中的模块重复会导致自定义错误类被加载两次,从而产生不同的原型。
解决方案:使用 error.name 或 error.constructor.name,而不是 instanceof:
// ❌ Wrong: instanceof doesn't work
try {
throw new CustomError('Test error');
} catch (error) {
if (error instanceof CustomError) { // ❌ false
// Never reached
}
}
// ✅ Correct: Use error.name
try {
throw new CustomError('Test error');
} catch (error) {
if (error instanceof Error && error.name === 'CustomError') { // ✅ true
// Handle CustomError
}
}
// ✅ Alternative: Use constructor.name
if (error.constructor.name === 'CustomError') {
// Handle CustomError
}
22. 错误:TypeScript 无法捕获传递给客户端组件的不可序列化属性
错误:向客户端组件传递函数/类实例时发生运行时错误 来源:GitHub Issue #86748
原因:Next.js TypeScript 插件无法捕获从服务端组件传递给客户端组件的不可序列化属性。这会导致编译时无法检测到的运行时错误。
发生原因:只有可序列化的数据(与 JSON 兼容)才能跨越服务端/客户端边界。函数、类实例和 Symbol 均无法序列化。
解决方案:仅传递可序列化的属性:
// ❌ Wrong: Function not serializable
const user = {
name: 'John',
getProfile: () => console.log('profile'), // ❌ Not serializable
};
<ClientComponent user={user} />
// ✅ Correct: Only serializable props
interface SerializableUser {
name: string;
email: string;
// No functions, no class instances, no Symbols
}
// ✅ Alternative: Create functions in Client Component
'use client';
export default function ClientComponent({ user }: { user: { name: string } }) {
const getProfile = () => console.log('profile'); // Define in client
return <div onClick={getProfile}>{user.name}</div>;
}
运行时验证:
import { z } from 'zod';
const UserSchema = z.object({
name: z.string(),
email: z.string(),
});
type User = z.infer<typeof UserSchema>;
23. 错误:Turbopack 使用 Prisma 时生产构建失败
错误:The 'path' argument must be of type string
来源:GitHub Discussion #77721
原因:使用 Prisma ORM(v6.5.0+)时,Turbopack 生产构建会失败。
解决方案:生产构建使用 webpack:
npm run build -- --webpack
或者在配置中禁用 Turbopack:
// next.config.ts
const config: NextConfig = {
experimental: {
turbo: false,
},
};
24. 错误:Turbopack 通过源映射暴露源代码
错误:生产构建中可见源代码 来源:GitHub Discussion #77721
原因:Turbopack 始终为浏览器构建生产源映射,从而暴露源代码。
解决方案:禁用生产源映射:
// next.config.ts
const config: NextConfig = {
productionBrowserSourceMaps: false,
};
或者在部署时排除 .map 文件:
# .vercelignore
*.map
25. 错误:生产环境中找不到模块(Turbopack monorepo)
错误:尽管本地构建成功,生产环境中仍出现 Module not found
来源:GitHub Issue #87737
原因:当 node_modules 结构不同时(pnpm、yarn workspaces、monorepos),Turbopack 生成的外部模块引用所用哈希值会与实际模块文件不匹配。
症状:
- 本地构建成功,但在 CI/CD 中失败
- 捆绑引用中的哈希值与实际模块文件不匹配
解决方案:显式将软件包设为外部依赖:
// next.config.ts
const config: NextConfig = {
experimental: {
serverExternalPackages: ['package-name'],
},
};
参阅参考资料:references/top-errors.md
模板与资源
Next.js 16 专用模板(位于 templates/):
app-router-async-params.tsx- 异步参数迁移模式parallel-routes-with-default.tsx- 必需的 default.js 文件cache-component-use-cache.tsx- 使用"use cache"的 Cache Componentsrevalidate-tag-cache-life.ts- 更新后的revalidateTag()与 cacheLifeserver-action-update-tag.ts- 用于读后写的updateTag()proxy-migration.ts- 从 middleware.ts 迁移到 proxy.tsview-transitions-react-19.tsx- React 19.2 View Transitionsnext.config.ts- Next.js 16 配置
随附参考资料(位于 references/):
next-16-migration-guide.md- 完整的 Next.js 15→16 迁移指南cache-components-guide.md- Cache Components 深入指南proxy-vs-middleware.md- Proxy.ts 与 middleware.tsasync-route-params.md- 异步参数破坏性变更详情react-19-integration.md- Next.js 16 中的 React 19.2 功能top-errors.md- 18+ 个常见错误及解决方案
外部文档:
- Next.js 16 博客:https://nextjs.org/blog/next-16
- Next.js 文档:https://nextjs.org/docs
- Context7 MCP:
/websites/nextjs,获取最新参考资料
版本兼容性
| 软件包 | 最低版本 | 推荐版本 |
|---|---|---|
| Next.js | 16.0.0 | 16.1.1+ |
| React | 19.2.0 | 19.2.3+ |
| Node.js | 20.9.0 | 20.9.0+ |
| TypeScript | 5.1.0 | 5.7.0+ |
| Turbopack | (内置) | 稳定版 |
检查版本:
./scripts/check-versions.sh
Token 效率
预计 Token 节省:65-70%
不使用技能(根据文档手动设置):
- 阅读 Next.js 16 迁移指南:约 5k 个 Token
- 阅读 App Router 文档:约 8k 个 Token
- 阅读 Server Actions 文档:约 4k 个 Token
- 阅读 Metadata API 文档:约 3k 个 Token
- 反复试错修复:约 8k 个 Token
- 总计:约 28k 个 Token
使用技能:
- 加载技能:约 8k 个 Token
- 使用模板:约 2k 个 Token
- 总计:约 10k 个 Token
- 节省:约 18k 个 Token(约 64%)
预防的错误:已记录的 25 个错误 = 100% 错误预防
维护
上次验证时间:2026-01-21 技能版本:3.1.0 变更:新增 7 个错误(导航节流、i18n 缓存、Turbopack 限制、instanceof 失效、不可序列化的 props)。扩展了异步 params codemod 的限制、缓存默认值和并行路由边缘情况。
下次审查:2026-04-21(每季度) 维护者:Jezweb | [email protected] 代码仓库:https://github.com/jezweb/claude-skills
更新触发条件:
- Next.js 主要/次要版本发布
- React 主要版本发布
- API 中的破坏性变更
- 新的 Turbopack 功能
版本检查:
cd skills/nextjs
./scripts/check-versions.sh
SKILL.md 结束