# 开发调试体系

> 高效的调试能力决定了开发速度的下限。本章覆盖 Next.js 16 开发中的四大调试场景:React DevTools 组件调试、Next.js 服务端断点调试、Turbopack 开发体验优化、以及 VS Code / Cursor 的高效配置。掌握这些工具,你的 debug 效率可以提升 3-5 倍。

## 1. React DevTools

### 1.1 安装与基础使用

React DevTools 是调试 React 组件的必备工具。安装 Chrome/Firefox 扩展后,开发者工具会多出两个 Tab:

- **Components**:查看组件树、Props、State、Hooks
- **Profiler**:分析组件渲染性能

```
Components Tab 功能:
├── 🔍 搜索组件(按名称/Props 过滤)
├── 👁️ 查看 Props / State / Hooks 实时值
├── ✏️ 实时编辑 Props 和 State
├── 🎯 点击组件 → 高亮 DOM 元素
└── 📍 点击 DOM 元素 → 定位到组件
```

### 1.2 Server Components 调试

在 Next.js 16 中,Server Components **不会出现在 React DevTools 中**——因为它们运行在服务端,不发送 JS 到浏览器。

如何区分 Server 和 Client Components:

```tsx
// ✅ 会出现在 DevTools 中(Client Component)
'use client'
export function ChatInput() { ... }

// ❌ 不会出现在 DevTools 中(Server Component)
export default async function ChatPage() { ... }
```

**调试 Server Components 的方法**:

1. **console.log** — 输出会出现在终端(不是浏览器控制台)
2. **Next.js 服务端断点**(下一节详解)
3. **React Server Components Payload 检查** — 浏览器 Network Tab 中搜索 `RSC` 或 `__next` 请求

### 1.3 Profiler:性能分析

Profiler 帮助你找出不必要的重渲染:

1. 打开 Profiler Tab → 点击录制按钮
2. 在页面上执行操作(输入、点击、导航)
3. 停止录制 → 查看 Flamegraph

```
关注指标:
├── Render Duration — 组件渲染耗时
├── Why did this render? — 触发渲染的原因
│   ├── Props changed
│   ├── State changed
│   ├── Parent rendered
│   └── Context changed
└── Highlight updates — 实时高亮重渲染的组件
```

::: tip 开启 "Highlight updates when components render"
Settings → General → 勾选此选项。每次组件重渲染时会闪烁高亮,能快速发现不必要的渲染。
:::

## 2. Next.js 服务端调试

### 2.1 Node.js Inspector 断点调试

Next.js 支持 Node.js Inspector 协议,可以在 VS Code 中打断点调试服务端代码:

```json
// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/next",
      "runtimeArgs": ["dev"],
      "skipFiles": ["<node_internals>/**"],
      "env": {
        "NODE_OPTIONS": "--inspect"
      }
    },
    {
      "name": "Next.js: debug client-side",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000"
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/next",
      "runtimeArgs": ["dev"],
      "serverReadyAction": {
        "pattern": "- Local:.+(https?://.+)",
        "uriFormat": "%s",
        "action": "debugWithChrome"
      }
    }
  ]
}
```

使用方法:
1. 在 Server Component / Route Handler / Server Action 代码中设置断点
2. 按 F5 启动 "Next.js: debug full stack"
3. 访问对应页面 → 断点命中 → 查看变量、调用栈

### 2.2 console 调试技巧

```tsx
// Server Component 中的 console.log → 输出到终端
export default async function ChatPage() {
  const chats = await db.query.chats.findMany()
  console.log('[ChatPage]', { count: chats.length }) // 终端
  return <ChatList chats={chats} />
}

// Client Component 中的 console.log → 输出到浏览器
'use client'
export function ChatInput() {
  console.log('[ChatInput] rendered') // 浏览器控制台
  return <input />
}
```

**推荐**:用结构化日志代替散装 `console.log`:

```typescript
// lib/logger.ts
export function log(context: string, data?: Record<string, unknown>) {
  if (process.env.NODE_ENV === 'development') {
    console.log(`[${new Date().toISOString()}] [${context}]`, data ?? '')
  }
}

// 使用
log('ChatPage', { userId: session.user.id, chatCount: chats.length })
// [2025-04-12T12:00:00.000Z] [ChatPage] { userId: 'xxx', chatCount: 42 }
```

### 2.3 错误追踪

```tsx
// app/error.tsx — 开发环境显示详细错误栈
'use client'

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <html>
      <body>
        <h2>Something went wrong!</h2>
        {process.env.NODE_ENV === 'development' && (
          <pre className="overflow-auto rounded bg-red-50 p-4 text-sm">
            {error.stack}
          </pre>
        )}
        <button onClick={reset}>Try again</button>
      </body>
    </html>
  )
}
```

## 3. Turbopack 开发体验

### 3.1 Turbopack 优势

Next.js 16 默认使用 Turbopack 作为开发构建器:

| 场景 | Webpack | Turbopack |
|------|---------|-----------|
| 冷启动 | 5-15s | 1-3s |
| 文件修改 HMR | 200-500ms | 10-50ms |
| 大页面首次编译 | 2-5s | 0.5-1s |
| 内存占用 | 500MB-2GB | 200-500MB |

### 3.2 HMR(Hot Module Replacement)

Turbopack 的 HMR 是**增量**的——只重新编译修改的模块及其直接依赖,不做全量构建:

```
修改 components/chat/chat-input.tsx
├── Turbopack 检测到变更
├── 只重新编译 chat-input.tsx(~10ms)
├── 通过 WebSocket 推送更新到浏览器
└── React Fast Refresh 替换组件(保持状态)
```

**Fast Refresh 保持状态的条件**:
- 文件只导出 React 组件
- 组件使用命名导出或默认导出
- 没有导出非组件的值(如常量、工具函数)

```tsx
// ✅ Fast Refresh 有效 — 编辑后保持 state
export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

// ❌ Fast Refresh 失效 — 编辑后状态重置(因为混合导出)
export const API_URL = '/api'
export default function Counter() { ... }
```

### 3.3 常见问题排查

```bash
# 1. Turbopack 编译错误不清晰 → 查看完整日志
NEXT_DEBUG_TURBOPACK=1 pnpm dev

# 2. HMR 不生效 → 检查文件是否混合导出
# 把非组件导出移到单独文件

# 3. 某些库不兼容 Turbopack → 临时回退 Webpack
pnpm dev --no-turbopack
```

## 4. VS Code / Cursor 高效配置

### 4.1 核心快捷键

| 快捷键 | 功能 | 场景 |
|--------|------|------|
| `Cmd+Shift+P` | 命令面板 | 一切操作的入口 |
| `Cmd+P` | 快速打开文件 | 输入文件名跳转 |
| `Cmd+Shift+F` | 全局搜索 | 搜索代码/文本 |
| `F12` | 跳转到定义 | 查看函数/组件源码 |
| `Shift+F12` | 查找所有引用 | 看谁调用了这个函数 |
| `F2` | 重命名符号 | 安全重命名变量/函数 |
| `Cmd+.` | Quick Fix | ESLint 修复、导入补全 |
| `Cmd+Shift+O` | 文件内符号搜索 | 跳转到函数/类/接口 |

### 4.2 Next.js 专属配置

```json
// .vscode/settings.json
{
  // TypeScript
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,

  // 格式化
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "explicit"
  },

  // Tailwind CSS 智能提示
  "tailwindCSS.experimental.classRegex": [
    ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
    ["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
    ["clsx\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
  ],

  // 文件关联
  "files.associations": {
    "*.css": "tailwindcss"
  },

  // Emmet 支持 JSX
  "emmet.includeLanguages": {
    "javascript": "javascriptreact",
    "typescript": "typescriptreact"
  },

  // 隐藏干扰文件
  "files.exclude": {
    "**/.next": true,
    "**/node_modules": true
  }
}
```

### 4.3 代码片段(Snippets)

```json
// .vscode/nextjs.code-snippets
{
  "Next.js Server Component": {
    "prefix": "nsc",
    "body": [
      "export default async function ${1:Component}() {",
      "  return (",
      "    <div>",
      "      $0",
      "    </div>",
      "  )",
      "}"
    ]
  },
  "Next.js Client Component": {
    "prefix": "ncc",
    "body": [
      "'use client'",
      "",
      "export default function ${1:Component}() {",
      "  return (",
      "    <div>",
      "      $0",
      "    </div>",
      "  )",
      "}"
    ]
  },
  "Server Action": {
    "prefix": "nsa",
    "body": [
      "'use server'",
      "",
      "import { z } from 'zod'",
      "import { revalidatePath } from 'next/cache'",
      "",
      "const ${1:Schema} = z.object({",
      "  $2",
      "})",
      "",
      "export async function ${3:action}(formData: FormData) {",
      "  const parsed = ${1:Schema}.safeParse(Object.fromEntries(formData))",
      "  if (!parsed.success) return { error: parsed.error.flatten() }",
      "",
      "  $0",
      "",
      "  revalidatePath('/')",
      "}"
    ]
  },
  "Route Handler": {
    "prefix": "nrh",
    "body": [
      "import { NextRequest, NextResponse } from 'next/server'",
      "",
      "export async function ${1|GET,POST,PUT,DELETE|}(request: NextRequest) {",
      "  $0",
      "  return NextResponse.json({ success: true })",
      "}"
    ]
  }
}
```

### 4.4 Cursor / AI 编辑器配置

如果你使用 Cursor,可以创建 `.cursorrules` 配置 AI 行为:

```
# .cursorrules
You are a Next.js 16 expert. Follow these rules:

- Use App Router (not Pages Router)
- Default to Server Components; only use 'use client' when needed
- Use TypeScript strict mode, never use 'as any'
- Use Zod for all external input validation
- Use Server Actions for data mutations
- Use shadcn/ui for UI components
- Use Tailwind CSS v4 for styling
- Follow the project directory structure: app/, components/, lib/, hooks/, types/
```

## 5. 开发工作流总结

### 5.1 日常开发循环

```
1. pnpm dev                    → Turbopack 启动(~1s)
2. 编辑 Server Component       → console.log 看终端
3. 编辑 Client Component       → Fast Refresh 保持状态
4. 遇到 Bug                    → F5 启动断点调试
5. 性能问题                     → React Profiler 分析
6. 提交前                       → pnpm lint && pnpm type-check
```

### 5.2 常用 package.json scripts

```json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "type-check": "tsc --noEmit",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "db:push": "drizzle-kit push",
    "db:studio": "drizzle-kit studio"
  }
}
```

## 本章小结

- **React DevTools**:Components Tab 查看组件树/Props/State,Profiler 分析渲染性能
- **Server Component 调试**:console.log 输出到终端,VS Code 断点调试服务端代码
- **Turbopack**:10ms 级 HMR,保持 Fast Refresh 有效的关键是不混合导出组件和非组件
- **VS Code 配置**:launch.json 断点调试 + 代码片段提效 + Tailwind 智能提示
- **开发工作流**:dev → edit → hot reload → debug → lint → commit

至此,第一篇「基础认知与工程初始化」全部完成。下一篇,我们将深入路由系统——App Router 的核心。