mcpField
N
nextjs

nextjs

github.com/ovachiever开发5665 次安装49 周前更新
代码仓库

关于 nextjs

nextjs skill 为使用 Next.js App Router 的开发者整理 Next.js 16 与 React 19.2 的生产实践,重点说明破坏性变更、缓存 API、迁移和错误处理。

主要功能

  1. 01Next.js 16 迁移指南
  2. 02异步路由参数处理
  3. 03Cache Components 与缓存 API
  4. 04Proxy 和并行路由配置
  5. 05常见错误及解决方案

适用场景

  1. 01将项目从 Next.js 15 迁移至 16
  2. 02为 App Router 配置缓存
  3. 03排查并行路由或异步参数错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ovachiever/droid-tings --skill nextjs

安装后在 Claude Code 中输入/nextjs确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
references/
next-16-migration-guide.md
top-errors.md
scripts/
check-versions.sh
templates/
app-router-async-params.tsx
cache-component-use-cache.tsx
package.json
parallel-routes-with-default.tsx
proxy-migration.ts
route-handler-api.ts
server-actions-form.tsx

详细介绍

Next.js App Router - 生产模式

版本:Next.js 16.0.0 React 版本:19.2.0 Node.js:20.9+ 上次验证:2025-10-24


目录

  1. 何时使用此技能
  2. 何时不使用此技能
  3. Next.js 16 重大变更
  4. 缓存组件与缓存 API
  5. 路由处理程序(Next.js 16 更新)
  6. Proxy 与 Middleware
  7. 并行路由 - 必须提供 default.js
  8. React 19.2 功能
  9. Turbopack(在 Next.js 16 中稳定)
  10. 常见错误与解决方案
  11. 模板与资源

何时使用此技能

重点: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、尺寸和质量)
  • 错误预防(18 个以上有文档记录的 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(可与此技能组合使用)

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 可自动迁移。

参见模板:templates/app-router-async-params.tsx


2. 中间件 → 代理迁移(破坏性变更)

破坏性变更:middleware.ts 在 Next.js 16 中已弃用。请改用 proxy.ts。

变更原因:proxy.ts 通过在 Node.js 运行时(而非 Edge 运行时)运行,明确了网络边界。这能更清晰地区分边缘中间件与服务端代理。

迁移步骤:

  1. 重命名文件:middleware.ts → proxy.ts
  2. 重命名函数:middleware → proxy
  3. 更新配置: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 中移除:

  1. AMP 支持 - 已完全移除。请迁移到标准页面。
  2. next lint 命令 - 请直接使用 ESLint 或 Biome。
  3. serverRuntimeConfig 和 publicRuntimeConfig - 请改用环境变量。
  4. experimental.ppr 标志 - 已演进为 Cache Components。请使用 "use cache" 指令。
  5. 自动添加 scroll-behavior: smooth - 如有需要,请手动添加。
  6. Node.js 18 支持 - 最低版本现为 20.9+。

迁移:

  • AMP:将 AMP 页面转换为标准页面,或使用独立的 AMP 实现。
  • 代码检查:直接运行 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 15Next.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


Cache Components 与缓存 API

Next.js 16 新增:Cache Components 通过 "use cache" 指令引入选择性启用的缓存,取代 Next.js 15 中的隐式缓存。

1. 概述

变更内容:

  • Next.js 15:隐式缓存(默认缓存所有服务器组件)
  • Next.js 16:选择启用缓存,使用 "use cache" 指令

变更原因:显式缓存让开发者拥有更多控制权,并使缓存行为更可预测。

Cache Components 支持:

  • 组件级缓存(缓存特定组件,而不是整个页面)
  • 函数级缓存(缓存开销较大的计算)
  • 页面级缓存(有选择地缓存整个页面)
  • Partial Prerendering(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. Partial Prerendering(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 配置),以实现过期后重新验证行为。

之前(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
}

内置 Cache Life 配置:

  • 'max'——最长过期时间(推荐用于大多数场景)
  • 'hours'——数小时后过期
  • 'days'——数天后过期
  • 'weeks'——数周后过期
  • 'default'——默认缓存行为

自定义 Cache Life 配置:

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(仅限服务器操作)

Next.js 16 新增:updateTag() 为服务器操作提供读后写语义。

功能:

  • 立即使缓存过期
  • 在同一请求中刷新数据
  • 在变更后立即显示更新的数据(不会显示过期数据)

与 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(仅限服务器操作)

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 更新)

路由处理程序中的异步 Params(重大变更)

重要:在 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,客户端导航时未匹配的插槽会报错。

参见模板: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

常见错误与解决方案

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. 错误:Route Group 路由冲突

错误:

Error: Conflicting routes: /about and /(marketing)/about

原因:Route Group 创建了相同的 URL 路径。

解决方案:确保 Route Group 之间不存在冲突:

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


模板与资源

Next.js 16 专用模板(位于 templates/):

  • app-router-async-params.tsx - 异步 params 迁移模式
  • parallel-routes-with-default.tsx - 必需的 default.js 文件
  • cache-component-use-cache.tsx - 使用 "use cache" 的 Cache Components
  • revalidate-tag-cache-life.ts - 使用 cacheLife 更新后的 revalidateTag()
  • server-action-update-tag.ts - 用于读后写一致性的 updateTag()
  • proxy-migration.ts - 从 middleware.ts 迁移到 proxy.ts
  • view-transitions-react-19.tsx - React 19.2 View Transitions
  • next.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.ts 的比较
  • async-route-params.md - 异步 params 破坏性变更详情
  • react-19-integration.md - Next.js 16 中的 React 19.2 功能
  • top-errors.md - 18 个以上常见错误及解决方案

外部文档:


版本兼容性

包最低版本推荐版本
Next.js16.0.016.0.0+
React19.2.019.2.0+
Node.js20.9.020.9.0+
TypeScript5.1.05.7.0+
Turbopack(内置)稳定版

检查版本:

./scripts/check-versions.sh

Token 效率

预计 Token 节省量:65-70%

不使用 Skill(根据文档手动配置):

  • 阅读 Next.js 16 迁移指南:约 5k tokens
  • 阅读 App Router 文档:约 8k tokens
  • 阅读 Server Actions 文档:约 4k tokens
  • 阅读 Metadata API 文档:约 3k tokens
  • 反复试错修复:约 8k tokens
  • 总计:约 28k tokens

使用 Skill:

  • 加载 Skill:约 8k tokens
  • 使用模板:约 2k tokens
  • 总计:约 10k tokens
  • 节省:约 18k tokens(约 64%)

预防错误:18 个以上常见错误,实现 100% 错误预防


维护

上次验证:2025-10-24 下次审核:2026-01-24(每季度) 维护者:Jezweb | [email protected] 代码仓库:https://github.com/jezweb/claude-skills

更新触发条件:

  • Next.js 主版本或次版本发布
  • React 主版本发布
  • API 中的破坏性变更
  • 新的 Turbopack 功能

版本检查:

cd skills/nextjs
./scripts/check-versions.sh

SKILL.md 结束

评论

支持 Markdown