一个 launch.json 无法同时调试服务端和客户端,因为 vscode 每个调试配置仅支持一种 type(node 或 chrome),协议与上下文完全隔离;next dev 启动后运行独立的 node 服务端进程和浏览器客户端进程,二者不共享堆栈、不可互通断点。

Next.js 项目里服务端和客户端代码混在同一个文件(比如 page.tsx)中,VSCode 默认配置只能调试其中一端——要么 Node 进程里的 getServerSideProps 或 API 路由,要么浏览器里的 React 组件逻辑。要同时断点,必须拆开配置、分进程 attach,且不能共用同一套 launch 配置。
为什么一个 launch.json 无法同时调试服务端和客户端
VSCode 的每个调试配置只支持一种 type:要么是 node(连 Node 进程),要么是 chrome(连 Chrome DevTools)。两者协议不同、上下文隔离。next dev 启动后会拉起至少两个独立进程:一个 Node.js 服务端(处理 SSR/API),一个 Webpack/Vite 构建的客户端 bundle(运行在浏览器)。它们不共享堆栈、不互通断点。
-
type: "node"配置只能看到服务端执行流,useEffect或事件回调永远不会命中 -
type: "chrome"配置看不到app/api/route.ts里的POST处理逻辑,控制台报Cannot find module是常态 - 强行把
program指向next dev命令本身(如"program": "npx next dev")会导致调试器卡死或报spawn ENOENT
服务端断点必须用 attach 模式连 9229 端口
Next.js 13+ 的 next dev 不是单进程脚本,而是主进程 fork 出多个 worker(RSC server、API handler、server actions runner)。直接 launch 只能 attach 到空壳主进程,业务代码根本没执行。
- 改
package.json的dev脚本为:"dev": "next dev --inspect=9229"(pnpm用户用pnpm exec next dev --inspect=9229) -
launch.json中必须用"request": "attach",不能是"launch" -
"port": 9229必须与--inspect参数一致;若端口被占,改成--inspect=0.0.0.0:9230并同步改port -
"localRoot"和"remoteRoot"必须严格相等,例如都设为"${workspaceFolder}",否则 TSX 断点灰掉 - 确保
next.config.js启用 source map:experimental: { sourceMaps: true }(Next.js 13.4+)
客户端断点得靠 Chrome 扩展 + webRoot 对齐
浏览器端调试依赖 Chrome DevTools 协议,VSCode 通过 Debugger for Chrome 插件桥接。关键不是“能不能连”,而是源码路径是否被正确映射。
- 安装官方插件:
Debugger for Chrome(注意不是Chrome Debugger或其他变体) -
launch.json新增配置,"type": "chrome","request": "launch","url": "http://localhost:3000" -
"webRoot"必须指向实际 serve 的静态资源根目录,默认是"${workspaceFolder}",但若你用outDir输出到.next/static/chunks,就得填"${workspaceFolder}/.next" - 断点打在
"use client"组件内才有效;服务端组件(如无"use client"标记的page.tsx)里的console.log会输出在终端,而非浏览器控制台 - 避免用
--inspect-brk:它会让 Node 进程卡在第一行,每次热重载都要手动 resume,拖慢开发节奏
复合调试(F5 一键启动双断点)要靠 compounds
VSCode 支持用 compounds 合并多个调试配置,但顺序和依赖必须显式声明:先让服务端跑起来,再启动浏览器。
- 在
launch.json根级加"compounds"字段,不能放在configurations里 - compound 名称任意,但
"configurations"数组里必须引用已定义的 name,例如:["Attach to Next.js", "Launch Chrome"] - 确保
"Attach to Next.js"配置的"restart": true已启用——热重载时旧 Node 进程退出,新进程自动重连 - Chrome 配置里加
"preLaunchTask": "npm: dev"(需提前在tasks.json定义)可强制先启服务,但更稳的方式是手动先跑npm run dev,再点 F5 - 如果 Chrome 启动后白屏或报
net::ERR_CONNECTION_REFUSED,说明服务端还没 ready,等终端出现event - compiled client and server successfully再触发 compound
真正麻烦的不是配 launch.json,而是每次修改 app/ 下的 layout/page 组件时,要判断当前逻辑跑在哪边:有 "use client"?用了 useEffect?调了 fetch?还是写在 getServerSideProps 里?这些决定你该切到哪个调试器、打在哪一行。混用场景下,断点失效往往不是配置错,而是你默认它该在浏览器停,其实它早就在 Node 进程里执行完了。











