在以前的文章 深入前端调试原理,我们主要从原理的角度来看了如何调试。本篇文章则是从实践角度出发,看看如何在 vscode 中配置和使用各种调试功能。
本文涉及到代码均在仓库 vscode-debugger-dojo,全面的 VSCode Debugger 调试示例项目,涵盖了各种常见场景的调试配置。
VSCode Debugger 原理
在 VSCode 的项目中 .vscode/launch.json 中加入如下的配置即可调试:
SCode 并不是 JS 语言的专属编辑器,它可以用于多种语言的开发,自然不能对某一种语言的调试协议进行深度适配,所以它提供了 Debugger 适配层和适配器协议,不同的语言可以通过提供 Debugger 插件的形式,增加 VSCode 对不同语言的调试能力:
如此,VSCode Debugger 就能以唯一的 Debugger 前端调试各个不同的语言,插件市场中也提供了诸多不同语言的调试插件:
因此需要使用 type 字段来配置对应的 Debugger 适配器,在前端场景主要是 node 或者 chrome,分别对应 node 和浏览器环境。
调试模式
调试模式通过 request 的方式指定如何启动和链接调试器。
Attach 模式
调试的前后端之间是通过 websocket 进行通信的,所以确保前后端能正确的连接是调试成功的关键。
除了在需要调试的网页中直接打开 Devtools 的方式外,我们使用第三方前端工具进行调试时,都需要知道所需要调试的网页的后端 ws 地址才行。因此我们需要让 Chrome 以指定调试端口的形式跑起来,使用 --remote-debugging-port 参数指定调试端口:
1/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 2
Chrome 运行参数非常多,可以通过 该文档 进行查看。
在 Chrome 运行起来后,随意的打开几个网页,然后访问 localhost:9222/json/list 网址,此时就能得到所有运行的网页后端 ws 地址:
在百度网页中同时打开了 Devtools, 可以看到连 Devtools 的调试信息都一起打印出来了,因为 Devtools 本质上也是一个网页程序,所以甚至可以做到对 Devtools Debugger 进行 Debug 这样的套娃操作。
有了 ws 信息,我们就可以使用其他 Debugger 进行连接了,比如使用下面的 VSCode Debug 配置:
Node.js 程序在运行时则是通过 --inspect 或者 --inspect-brk 参数开启调试模式:
Node.js 调试协议经过了漫长的迭代,最终也是以 CDP 协议进行调试,因此 Node.js 程序可以直接使用 Devtools 进行调试,在 Chrome 中访问 chrome://inspect/#devices 就可以看到调试目标了:
如果当前的 Node 项目没有被发现,可能是检测端口不是默认的
9229,可以通过Configure进行配置。
VSCode Debugger 同样能够连接上 Node.js 项目并进行调试,
Launch 模式
前面讲到的都是首先通过调试模式启动一个项目,然后再手动进行前端 ws 连接,最后再调试的模式。相对较繁琐,VSCode Debugger 提供了 launch 模式,它相当于是将上面的流程自动化了,以调试模式将 Chrome 或者 Node.js 程序运行起来,然后 Debugger 自动 attach 到调试端口,直接调试。
常见配置
launch.json 核心字段
每个调试配置都需要以下基础字段:
| 字段 | 说明 | 可选值 / 示例 |
|---|---|---|
| type | 调试器类型 | node、chrome、lldb 等 |
| request | 调试模式 | launch(启动)、attach(附加) |
| name | 配置名称 | 任意描述性名称 |
常用配置项
Node.js 调试配置项
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Node.js Debug", 5 "program": "${workspaceFolder}/index.js", 6 "args": ["--port", "3000"], 7 "cwd": "${workspaceFolder}", 8 "env": { 9 "NODE_ENV": "development" 10 }, 11 "skipFiles": ["<node_internals>/**", "**/node_modules/**"], 12 "console": "integratedTerminal", 13 "runtimeExecutable": "node", 14 "runtimeArgs": ["-r", "ts-node/register"] 15} 16
配置说明:
program:程序入口文件args:传递给程序的命令行参数cwd:工作目录env:环境变量skipFiles:调试时跳过的文件(如 node 内部代码)console:终端类型(integratedTerminal、internalConsole、externalTerminal)runtimeExecutable:运行时可执行文件(如node、npm、pnpm)runtimeArgs:传递给运行时的参数
Chrome 调试配置项
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Chrome Debug", 5 "url": "http://localhost:3000", 6 "webRoot": "${workspaceFolder}", 7 "userDataDir": "${workspaceFolder}/.chrome-data", 8 "runtimeArgs": ["--auto-open-devtools-for-tabs"], 9 "sourceMaps": true, 10 "sourceMapPathOverrides": { 11 "webpack:///./~/*": "${webRoot}/node_modules/*", 12 "webpack:///./*": "${webRoot}/*" 13 } 14} 15
配置说明:
url:要打开的网页地址webRoot:Web 根目录,用于源码映射,本质上就是对sourcemapPathOverrides的快捷配置方式userDataDir:Chrome 用户数据目录,保存登录状态和扩展runtimeArgs:传递给 Chrome 的启动参数sourceMaps:是否启用 Source MapsourceMapPathOverrides:源码路径映射覆盖规则
Compound 配置
使用 compounds 可以同时启动多个调试会话:
1{ 2 "compounds": [ 3 { 4 "name": "Full Stack Debug", 5 "configurations": ["Server", "Client"], 6 "stopAll": true 7 } 8 ] 9} 10
VSCode 变量
在配置中可以使用以下变量:
| 变量 | 说明 |
|---|---|
| ${workspaceFolder} | 工作区根目录 |
| ${file} | 当前打开的文件完整路径 |
| ${relativeFile} | 相对于工作区的文件路径 |
| ${fileBasename} | 文件名(含扩展名) |
| ${fileBasenameNoExtension} | 文件名(不含扩展名) |
| ${fileDirname} | 文件所在目录 |
| ${cwd} | 当前工作目录 |
| ${env:NAME} | 环境变量 NAME 的值 |
可参考官方文档:Variables reference
输入变量
使用 inputs 可以在启动调试时要求用户输入:
1{ 2 "configurations": [ 3 { 4 "name": "Debug with args", 5 "args": ["${input:testName}"] 6 } 7 ], 8 "inputs": [ 9 { 10 "id": "testName", 11 "type": "promptString", 12 "description": "输入测试名称", 13 "default": "test_example" 14 } 15 ] 16} 17
典型场景
Vite + React 项目
在常规的 React 项目中,通常可以通过先启动完 vite dev 项目后,再运行如下配置启动调试:
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Vite React", 5 "url": "http://localhost:5173", 6 "webRoot": "${workspaceFolder}/packages/vite-react-demo", 7 "runtimeArgs": ["--auto-open-devtools-for-tabs"] 8} 9
保存登录状态
但如此一次,每次启动的 chrome 都是一个全新的实例,没有任何的插件或者登录信息。如果我们希望每次启动的 chrome 都是带上 react 插件,并且保留登录信息,可以通过 userDataDir 自定用户信息目录,然后在第一次安装插件后,后续就无需在处理了:
1{ 2 "userDataDir": "${workspaceFolder}/packages/vite-react-demo/.chrome-data" 3} 4
任务集成
如果不希望单独运行 vite dev,而是在每次启动调试时,自行运行 vite dev 后再开启调试,可以通过 preLaunchTask 和 postDebugTask 可以在调试前后执行任务:
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Vite React Debug", 5 "url": "http://localhost:5173", 6 "preLaunchTask": "vite-react: dev", 7 "postDebugTask": "kill-vite-react-dev" 8} 9
需要在 .vscode/tasks.json 中定义对应的任务:
1{ 2 "version": "2.0.0", 3 "tasks": [ 4 { 5 "label": "vite-react: dev", 6 "type": "npm", 7 "script": "dev", 8 "isBackground": true, 9 "problemMatcher": { 10 "background": { 11 "activeOnStart": true, 12 "beginsPattern": ".", 13 // 说明 task 准备完成 14 "endsPattern": "ready in" 15 } 16 } 17 } 18 ] 19} 20
Task 配置属于锦上添花,不是调试过程中的必须配置,更多信息可以参考官网 Integrate with External Tools via Tasks。
Next.js 全栈调试
Next.js 同时包含客户端和服务端代码,所以我们的调试场景既包含了 node 端,也包含了 chrome 端。可以借助 serverReadyAction 和 compounds 两种方式同时连接 node ,web 端 debugger 调试器。
serverReadyAction 一个强大的自动化调试配置,它允许你在服务器启动后,自动触发一个额外的任务,比如针对 Chrome 浏览器的调试会话,从而实现 全栈调试。
而 compounds 则是同时启动多个调试,但是不处理调试之间的依赖关系。
serverReadyAction:debugWithChrome
在启动完成 node 后端后,如果 node 控制台检测到输出了 pattern 内容,则开启一个 chrome 调试窗口:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Next.js - Full Stack", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["next", "dev"], 7 "serverReadyAction": { 8 "pattern": "Local:\s+(https?://.+)", 9 "action": "debugWithChrome", 10 "uriFormat": "%s", 11 "webRoot": "${workspaceFolder}" 12 }, 13 "cwd": "${workspaceFolder}", 14 "console": "integratedTerminal", 15 "skipFiles": ["<node_internals>/**"] 16} 17
但是该模式下对 chrome 调试器的配置很有限,比如 userDataDir 都无法配置,适用于在开启后端调试后,同时兼顾简单的前端调试。
serverReadyAction:startDebugging
startDebugging 则更强大,可以在服务器启动后自动启动另一个客户端调试配置,这样客户端的配置就是完整的,可以完成使用各种标准配置
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Next.js - Server", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["next", "dev"], 7 "serverReadyAction": { 8 "pattern": "Local:\s+(https?://.+)", 9 "action": "startDebugging", 10 "name": "Next.js - Client" 11 }, 12 "cwd": "${workspaceFolder}", 13 "console": "integratedTerminal", 14 "skipFiles": ["<node_internals>/**"] 15} 16
配合的客户端配置:
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Next.js - Client", 5 "url": "http://localhost:3000", 6 "webRoot": "${workspaceFolder}" 7} 8
Compounds
不使用 serverReadyAction 时,通常单独各个端的调试配置,然后通过 compounds 同时启动多个独立的调试会话:
1{ 2 "compounds": [ 3 { 4 "name": "Next.js - Full Stack", 5 "configurations": ["Next.js - Server", "Next.js - Client"], 6 "stopAll": true 7 } 8 ] 9} 10
三种方式对比:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| debugWithChrome | 配置简单,一键启动 | 调试会话合并,控制粒度较粗 | 日常开发 |
| startDebugging | 自动化程度高,会话独立 | 需要两个配置 | 需要独立控制前后端 |
| Compound | 完全手动控制,灵活性高 | 需要手动管理多个会话 | 复杂调试场景 |
serverReadyAction还有一种 actionopenExternally,它的作用更为直接,会在 server 启动后,直接打开在 chrome 打开一个页面,但是不连接客户端调试,只是为了方便交互客户端后,调试服务端。
基本上涉及到多端调试的场景都可以用类似的方案,比如 electron, express 前后端 mono repo 项目等等。
Node 项目
TypeScript 项目
在开发 node ts 项目时,除了编译完成后在运行 js 程序,也可以采用 tsx 或者 ts-node 直接运行 ts 程序,它们会在项目运行前自动进行编译操作。
需要在 runtimeArgs 增加 -r 参数来使用,也可以直接将 runtimeExecutable 进行替换。
使用 tsx(推荐,启动快):
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "TypeScript - Current File (tsx)", 5 "program": "${file}", 6 // 或者 7 // "runtimeExecutable": "tsx", 8 "runtimeArgs": ["-r", "tsx/cjs"], 9 "cwd": "${workspaceFolder}", 10 "console": "integratedTerminal", 11 "skipFiles": ["<node_internals>/**"] 12} 13
使用 ts-node(完整类型检查):
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "TypeScript - Current File (ts-node)", 5 "program": "${file}", 6 // 或者 7 // "runtimeExecutable": "ts-node", 8 "runtimeArgs": ["-r", "ts-node/register"], 9 "cwd": "${workspaceFolder}", 10 "console": "integratedTerminal", 11 "skipFiles": ["<node_internals>/**"], 12 "env": { 13 "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" 14 } 15} 16
Pnpm 管理项目
在 npm/yarn 管理的 Node 项目中,通常可以直接通过 program 指定 node_modules/.bin 下的命令文件:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest", 5 "program": "${workspaceFolder}/node_modules/.bin/jest" 6} 7
但在 pnpm 项目中,这种方式会失败。
问题根源: pnpm 出于性能和磁盘空间考虑,使用了独特的依赖管理方式,将 .bin 目录下的文件转化为 shell 脚本:
当尝试用 node 直接执行这些文件时,会报错:
1SyntaxError: Invalid or unexpected token 2
因为 Node.js 无法直接执行 shell 脚本文件。
方案 1:使用 pnpm 执行 - 将 runtimeExecutable 改为 pnpm,通过 runtimeArgs 传递命令:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest - pnpm", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["jest", "--runInBand"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
方案 2:直接指向 JS 文件 - 绕过 shell 脚本,直接指向包内的 JavaScript 入口文件:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest - direct", 5 "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", 6 "args": ["--runInBand"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
方案 3:使用 sh 执行 - 让 sh 来执行 shell 脚本:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest - sh", 5 "runtimeExecutable": "sh", 6 "program": "${workspaceFolder}/node_modules/.bin/jest", 7 "args": ["--runInBand"], 8 "cwd": "${workspaceFolder}", 9 "console": "integratedTerminal", 10 "skipFiles": ["<node_internals>/**"] 11} 12
实际应用示例:
调试 Vitest 当前文件:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Vitest - Current File", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["vitest", "run", "${relativeFile}"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
调试 ESLint:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "ESLint - Current File", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["eslint", "${file}", "--fix"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal" 9} 10
使用直接路径调试 TypeScript:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "TSC - Build", 5 "program": "${workspaceFolder}/node_modules/typescript/bin/tsc", 6 "args": ["--noEmit"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal" 9} 10
单测
Jest 单元测试
在运行单侧时,通常有两种场景,运行所有单侧,和运行当前单侧文件的单测。
调试当前打开的测试文件:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest - Current File", 5 "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", 6 "args": [ 7 "${relativeFile}", 8 "--config=${workspaceFolder}/jest.config.js", 9 "--runInBand", 10 "--no-coverage" 11 ], 12 "cwd": "${workspaceFolder}", 13 "console": "integratedTerminal", 14 "internalConsoleOptions": "neverOpen" 15} 16
关键参数:
--runInBand:串行运行测试(调试必需)--no-coverage:禁用覆盖率收集,加快调试速度
调试所有测试:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Jest - All Tests", 5 "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", 6 "args": ["--runInBand", "--config=${workspaceFolder}/jest.config.js"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal" 9} 10
Vitest 单元测试
调试当前测试文件:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Vitest - Current File", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["vitest", "run", "${file}"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
监视模式:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Vitest - Watch Mode", 5 "runtimeExecutable": "pnpm", 6 "runtimeArgs": ["vitest"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
npm script 调试
通常,我们会将实际运行的程序写入到 npm scripts 中,所以也通过通过以下方式直接调试 script 脚本,就不需要在 launch.json 中重定义一套配置了:
方式一:使用 node 类型:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "npm script - Start", 5 "runtimeExecutable": "npm", 6 "runtimeArgs": ["run-script", "start"], 7 "cwd": "${workspaceFolder}", 8 "console": "integratedTerminal", 9 "skipFiles": ["<node_internals>/**"] 10} 11
方式二:使用 node-terminal 类型:
1{ 2 "type": "node-terminal", 3 "name": "npm script - Start (Terminal)", 4 "request": "launch", 5 "command": "npm start", 6 "cwd": "${workspaceFolder}" 7} 8
Puppeteer 自动化调试
有些时候,调试准备调试数据,尤其是表单类的应用,除了在代码中进行处理,也可以借助 puppeteer 自动化前置的流程,直达要调试的阶段。
通过脚本 启动 puppeteer ,并搭配 --remote-debugging-port=9222 开启调试端口,在启动 puppeteer 后,通过 attach 模式连接 chrome 就可以自动化前置内容填写和连接 chrome 调试器两个阶段:
调试 Puppeteer 脚本:
1{ 2 "type": "node", 3 "request": "launch", 4 "name": "Puppeteer - Script", 5 "program": "${workspaceFolder}/scripts/auto-fill-form.js", 6 "cwd": "${workspaceFolder}", 7 "console": "integratedTerminal", 8 "skipFiles": ["<node_internals>/**"], 9 "env": { 10 "HEADLESS": "false" 11 } 12} 13
Chrome Attach 配置:
1{ 2 "type": "chrome", 3 "request": "attach", 4 "name": "Puppeteer - Chrome Attach", 5 "port": 9222, 6 "webRoot": "${workspaceFolder}" 7} 8
同时调试 Puppeteer 脚本和浏览器代码:
1{ 2 "compounds": [ 3 { 4 "name": "Puppeteer - Full Stack", 5 "configurations": ["Puppeteer - Script", "Puppeteer - Chrome Attach"], 6 "stopAll": true 7 } 8 ] 9} 10
puppeteer 脚本可以直接借助 ai 来生成,编写成本也非常低
快捷调试方式
除了使用 launch.json 配置外,VSCode 也提供了很多便捷的开启调试的手段。相对于通过 ui 交互简化了配置过程。
Package.json 调试按钮
VSCode 会在 package.json 的 scripts 上方显示调试按钮,点击即可快速调试脚本。
使用方式:
- 打开
package.json文件 - 在 scripts 字段上方会出现 "Debug" 按钮
- 点击按钮即可开始调试
Testing 面板
VSCode 提供了统一的测试面板,支持 Jest、Vitest、Mocha 等测试框架。
启用方式:
- 安装对应的测试扩展(如 Jest Runner、Vitest)
- 打开测试面板(左侧活动栏的烧杯图标)
- VSCode 会自动发现项目中的测试
可以非常便捷的在面板完成单侧的运行。
并且在单测文件中,每个单测也有便捷的调试按钮:
Auto Attach
Auto Attach 是 VSCode 提供的一个非常方便的功能,可以自动附加调试器到在集成终端中启动的 Node.js 进程,可以在配置中打开:
三种模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| Smart | 自动检测常见脚本,如 npm scripts | 推荐,日常使用 |
| Always | 附加到所有 Node.js 进程 | 调试所有 Node 程序 |
| Only With Flag | 仅附加到使用 --inspect 标志的进程 | 精确控制 |
使用示例:
启用 Auto Attach 后,在集成终端中直接运行:
1node src/index.js 2# 或 3npm run dev 4# 或 5pnpm test 6
调试器会自动附加。
参考:auto-attach
经典问题
为什么断点不生效
最常见的是为什么明明设置了断点,调试器也连接了,但是就是没有停住。此时添加的断点是灰色的:
可以鼠标 hover 断点进行简单诊断:
常见原因:
launch.json 配置关闭了 sourcemap
sourceMaps 默认是 true, 如果改为了 false 就不会加载 sourcemap 了
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Vite React - Launch (自定义用户信息)", 5 "url": "http://localhost:5173", 6 "runtimeArgs": ["--auto-open-devtools-for-tabs"], 7 "sourceMaps": false 8} 9
Source Map 配置错误
确保构建工具正确生成了 Source Map:
TypeScript (tsconfig.json):
1{ 2 "compilerOptions": { 3 "sourceMap": true, 4 "inlineSourceMap": false, 5 "sourceRoot": "" 6 } 7} 8
Vite (vite.config.ts):
1export default { 2 build: { 3 sourcemap: true, 4 }, 5}; 6
Webpack (webpack.config.js):
1module.exports = { 2 devtool: "source-map", // 或 'eval-source-map' 用于开发 3}; 4
源码路径映射错误
即使所有的 sourcemap 配置都正常,也可能会出现问题,常见于 webpack。比如 webpack 生成的 sourcemap 是:
映射出的原始文件以 webpack:// 开头,比如 webpack://webpack-demo/./src/app.ts,而我们打的断点在 ${workspaceFolder}/packages/webpack-demo/src/app.ts。VSCode 不知道两者关系,此时需要 sourceMapPathOverrides 和 webRoot 修正 Source Map 路径与本地文件路径的映射。
配置说明:
- webRoot 的作用 -
webRoot指定 Web 应用的源码根目录,作为 Source Map 路径映射的基准路径。在sourceMapPathOverrides中可以通过${webRoot}变量引用这个基准路径。 - sourceMapPathOverrides 配置 - 当 Source Map 中的路径与本地文件路径不匹配时,使用
sourceMapPathOverrides修正路径映射:
1{ 2 "type": "chrome", 3 "request": "launch", 4 "name": "Chrome Debug", 5 "url": "http://localhost:3000", 6 "webRoot": "${workspaceFolder}/packages/webpack-demo", 7 // 相当于: 8 "sourceMapPathOverrides": { 9 "webpack://webpack-demo/./src/*": "${webRoot}/src/*", 10 "webpack://webpack-demo/./*": "${webRoot}/*" 11 } 12} 13
默认配置:
如果不配置 sourceMapPathOverrides,VSCode 会使用以下默认规则 (适用于 Chrome/浏览器调试):
1{ 2 "sourceMapPathOverrides": { 3 "webpack:///./~/*": "${webRoot}/node_modules/*", 4 "webpack:///./*": "${webRoot}/*", 5 "webpack:///*": "*", 6 "webpack:///src/*": "${webRoot}/*", 7 "meteor://💻app/*": "${webRoot}/*" 8 } 9} 10
注意: 对于 Node.js 调试,默认规则中使用
${workspaceFolder}替代${webRoot}。
配置说明:
- 左侧是 Source Map 中的路径模式
- 右侧是本地文件系统的实际路径
*是通配符,匹配任意路径片段- 映射规则从上到下匹配,使用第一个匹配的规则
常见打包工具的配置示例:
- Webpack 项目:
1{ 2 "sourceMapPathOverrides": { 3 "webpack:///./*": "${webRoot}/*", 4 "webpack:///src/*": "${webRoot}/src/*", 5 "webpack:///*": "${webRoot}/*" 6 } 7} 8
2. Vite 项目:
1Vite 的 Source Map 路径通常已经是正确的,一般不需要额外配置。但如果遇到问题: 2
1{ 2 "sourceMapPathOverrides": { 3 "/@fs/*": "/*", 4 "/src/*": "${webRoot}/src/*" 5 } 6} 7
3. Monorepo 项目:
1{ 2 "webRoot": "${workspaceFolder}/packages/your-app", 3 "sourceMapPathOverrides": { 4 "webpack:///packages/your-app/*": "${webRoot}/*", 5 "webpack:///../your-app/*": "${webRoot}/*" 6 } 7} 8
假设:
webRoot = "${workspaceFolder}/packages/app"- Source Map 路径:
webpack://app/./src/index.ts
映射规则:
1{ 2 "sourceMapPathOverrides": { 3 "webpack://app/./*": "${webRoot}/*" 4 } 5} 6
映射过程:
webpack://app/./src/index.ts匹配规则webpack://app/./**匹配到src/index.ts- 替换为
${webRoot}/src/index.ts - 展开
${webRoot}得到最终路径:${workspaceFolder}/packages/app/src/index.ts
调试器启动慢
原因:
- 项目文件过多
- Source Map 文件过大
- skipFiles 配置不当
优化方法:
- 跳过不必要的文件
1{ 2 "skipFiles": [ 3 "<node_internals>/**", 4 "**/node_modules/**", 5 "${workspaceFolder}/dist/vendor.js" 6 ] 7} 8
2. 限制 Source Map 查找范围
1{ 2 "outFiles": ["${workspaceFolder}/dist/**/*.js", "!**/node_modules/**"] 3} 4
3. 使用更快的 Source Map 类型
1开发环境使用 [`eval-source-map`](https://xplanc.org/primers/document/zh/02.Python/EX.%E5%86%85%E5%BB%BA%E5%87%BD%E6%95%B0/EX.eval.md): 2
1// webpack.config.js 2devtool: "eval-source-map"; // 比 'source-map' 快但体积大 3
总结
vscode debugger 是非常强大的,可以极大提升调试效率。并且设置虽多,关键的就是那几个,配置也不麻烦。了解了 vscode debugger 能做哪些事情后,搭配 AI, 更是进一步简化了配置的难度。
欢迎关注笔者的个人公众号,共同学习,共同前进
《VSCode debugger 调试指南》 是转载文章,点击查看原文。