在 VS Code 中调试 Node.js
Visual Studio Code 编辑器内置了对 Node.js 运行时的调试支持,并且可以调试 JavaScript、TypeScript 以及许多其他可转译为 JavaScript 的语言。使用 VS Code 为 Node.js 调试设置项目非常简单,因为它提供了适当的启动配置默认值和代码片段。
您可以通过以下几种方式在 VS Code 中调试您的 Node.js 程序:
- 使用自动附加 (auto attach) 来调试在 VS Code 集成终端中运行的进程。
- 使用JavaScript 调试终端,这与使用集成终端类似。
- 使用启动配置来启动您的程序,或附加到在 VS Code 外部启动的进程。
自动附加 (Auto Attach)
如果自动附加 (Auto Attach) 功能已启用,Node 调试器会自动附加到从 VS Code 集成终端启动的某些 Node.js 进程。要启用此功能,可以从命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 使用切换自动附加 (Toggle Auto Attach) 命令,或者如果已激活,使用自动附加 (Auto Attach) 状态栏项。
自动附加有三种模式,您可以在弹出的“快速选择”中和通过 debug.javascript.autoAttachFilter 设置进行选择:
smart
(智能)- 如果您在node_modules
文件夹之外执行脚本,或使用常见的“运行器”脚本(如 mocha 或 ts-node),该进程将被调试。您可以使用 自动附加智能模式 (Auto Attach Smart Pattern) 设置 (`debug.javascript.autoAttachSmartPattern`) 配置“运行器”脚本的允许列表。always
(总是)- 在集成终端中启动的所有 Node.js 进程都将被调试。onlyWithFlag
(仅带标志)- 只有使用--inspect
或--inspect-brk
标志启动的进程才会被调试。
启用自动附加 (Auto Attach) 后,您需要通过单击终端右上角的 ⚠ 图标来重启终端,或者直接创建一个新终端。然后,调试器应该在一秒钟内附加到您的程序。
当自动附加开启时,`Auto Attach` 项会出现在 VS Code 窗口底部的状态栏中。单击它可以更改自动附加模式,或暂时关闭它。如果您运行一些不需要调试的一次性程序,但又不想完全禁用该功能,则暂时关闭自动附加会很有用。
其他配置
其他启动配置属性
您可以将通常在 launch.json 中找到的其他属性应用于 debug.javascript.terminalOptions 设置中的自动附加。例如,要将 Node 内部模块添加到您的 skipFiles 中,您可以将以下内容添加到您的用户或工作区设置中:
"debug.javascript.terminalOptions": {
"skipFiles": [
"<node_internals>/**"
]
},
自动附加智能模式
在 smart
(智能)自动附加模式下,VS Code 将尝试附加到您的代码,而不会附加到您不感兴趣的构建工具。它通过将主脚本与 glob 模式列表进行匹配来实现这一点。glob 模式可在 debug.javascript.autoAttachSmartPattern 设置中配置,其默认值为:
[
'!**/node_modules/**', // exclude scripts in node_modules folders
'**/$KNOWN_TOOLS$/**' // but include some common tools
];
$KNOWN_TOOLS$
被替换为常见的“代码运行器”列表,例如 ts-node
、mocha
、ava
等。如果这些设置不起作用,您可以修改此列表。例如,要排除 mocha
并包含 my-cool-test-runner
,您可以添加两行:
[
'!**/node_modules/**',
'**/$KNOWN_TOOLS$/**',
'!**/node_modules/mocha/**', // use "!" to exclude all scripts in "mocha" node modules
'**/node_modules/my-cool-test-runner/**' // include scripts in the custom test runner
];
JavaScript 调试终端
与自动附加类似,JavaScript 调试终端将自动调试您在其内部运行的任何 Node.js 进程。您可以通过从命令面板 (kbs(workbench.action.showCommands)
) 运行调试:创建 JavaScript 调试终端 (Debug: Create JavaScript Debug Terminal) 命令,或从终端切换器下拉菜单中选择创建 JavaScript 调试终端 (Create JavaScript Debug Terminal) 来创建调试终端。
其他配置
其他启动配置属性
您可以将通常在 launch.json 中找到的其他属性应用于 debug.javascript.terminalOptions 设置中的调试终端。例如,要将 Node 内部模块添加到您的 skipFiles 中,您可以将以下内容添加到您的用户或工作区设置中:
"debug.javascript.terminalOptions": {
"skipFiles": [
"<node_internals>/**"
]
},
启动配置
启动配置是在 VS Code 中设置调试的传统方式,它为您提供了运行复杂应用程序的最多的配置选项。
在本节中,我们将更详细地介绍用于更高级调试场景的配置和功能。您将找到关于使用源映射进行调试、跳过外部代码、执行远程调试等等的说明。
如果您想观看入门视频,请参阅 VS Code 调试入门。
注意:如果您刚开始使用 VS Code,可以在调试主题中了解通用的调试功能以及如何创建
launch.json
配置文件。
启动配置属性
调试配置存储在您工作区的 .vscode
文件夹中的 launch.json
文件中。关于调试配置文件的创建和使用的介绍可以在一般的调试文章中找到。
以下是 Node.js 调试器特有的常见 launch.json
属性参考。您可以在 vscode-js-debug 选项文档中查看完整的选项集。
以下属性在类型为 launch
和 attach
的启动配置中受支持:
outFiles
- 用于定位生成的 JavaScript 文件的 glob 模式数组。请参阅源映射部分。resolveSourceMapLocations
- 用于解析源映射位置的 glob 模式数组。请参阅源映射部分。timeout
- 在重新启动会话时,在此毫秒数后放弃。请参阅附加到 Node.js 部分。stopOnEntry
- 程序启动时立即中断。localRoot
- VS Code 的根目录。请参阅下面的远程调试部分。remoteRoot
- Node 的根目录。请参阅下面的远程调试部分。smartStep
- 尝试自动跳过未映射到源文件的代码。请参阅智能步进部分。skipFiles
- 自动跳过这些 glob 模式覆盖的文件。请参阅跳过不相关的代码部分。trace
- 启用诊断输出。
这些属性仅适用于请求类型为 launch
的启动配置:
program
- 要调试的 Node.js 程序的绝对路径。args
- 传递给要调试的程序的参数。此属性为数组类型,期望将单个参数作为数组元素。cwd
- 在此目录中启动要调试的程序。runtimeExecutable
- 要使用的运行时可执行文件的绝对路径。默认值为node
。请参阅对“npm”和其他工具的启动配置支持部分。runtimeArgs
- 传递给运行时可执行文件的可选参数。runtimeVersion
- 如果使用 "nvm" (或 "nvm-windows") 或 "nvs" 来管理 Node.js 版本,则此属性可用于选择特定版本的 Node.js。请参阅下面的多版本支持部分。env
- 可选的环境变量。此属性期望环境变量为字符串类型的键/值对列表。envFile
- 包含环境变量定义的文件可选路径。请参阅下面的从外部文件加载环境变量部分。console
- 启动程序的控制台 (internalConsole
、integratedTerminal
、externalTerminal
)。请参阅下面的Node 控制台部分。outputCapture
- 如果设置为std
,进程的 stdout/stderr 输出将显示在调试控制台中,而不是通过调试端口监听输出。这对于直接写入 stdout/stderr 流而不是使用console.*
API 的程序或日志库非常有用。
此属性仅适用于请求类型为 attach
的启动配置:
restart
- 在终止时重新启动连接。请参阅自动重新启动调试会话部分。port
- 要使用的调试端口。请参阅附加到 Node.js 和远程调试部分。address
- 调试端口的 TCP/IP 地址。请参阅附加到 Node.js 和远程调试部分。processId
- 调试器在发送 USR1 信号后尝试附加到此进程。通过此设置,调试器可以附加到未在调试模式下启动的已运行进程。使用processId
属性时,调试端口将根据 Node.js 版本(和使用的协议)自动确定,不能显式配置。因此,请勿指定port
属性。continueOnAttach
- 当我们附加到进程时,如果进程已暂停,是否继续执行。如果您使用--inspect-brk
启动程序,此选项会很有用。
常见场景的启动配置
您可以在 launch.json
文件中触发 IntelliSense (⌃Space (Windows, Linux Ctrl+Space)) 以查看常用 Node.js 调试场景的启动配置代码片段。
您还可以通过 launch.json
编辑器窗口右下角的添加配置... (Add Configuration...) 按钮调出这些代码片段。
以下代码片段可用:
- 启动程序 (Launch Program):在调试模式下启动 Node.js 程序。
- 通过 npm 启动 (Launch via npm):通过 npm 的 'debug' 脚本启动 Node.js 程序。如果 npm 调试脚本已在您的 package.json 中定义,您就可以在启动配置中使用它。npm 脚本中使用的调试端口必须与代码片段中指定的端口一致。
- 附加 (Attach):附加到本地运行的 Node.js 程序的调试端口。请确保要调试的 Node.js 程序已在调试模式下启动,并且使用的调试端口与代码片段中指定的端口相同。
- 附加到远程程序 (Attach to Remote Program):附加到在
address
属性指定的宿主机上运行的 Node.js 程序的调试端口。请确保要调试的 Node.js 程序已在调试模式下启动,并且使用的调试端口与代码片段中指定的端口相同。为了帮助 VS Code 映射您的工作区和远程宿主机文件系统之间的源文件,请务必为localRoot
和remoteRoot
属性指定正确的路径。 - 按进程 ID 附加 (Attach by Process ID):打开进程选择器以选择一个 node 或 gulp 进程进行调试。使用此启动配置,您甚至可以附加到未在调试模式下启动的 node 或 gulp 进程。
- Nodemon 设置 (Nodemon Setup):使用 nodemon 在 JavaScript 源文件更改时自动重新启动调试会话。请确保您已全局安装 nodemon。请注意,终止调试会话只会终止要调试的程序,而不会终止 nodemon 本身。要终止 nodemon,请在集成终端中按 Ctrl+C。
- Mocha 测试 (Mocha Tests):调试您项目
test
文件夹中的 mocha 测试。请确保您的项目已在其node_modules
文件夹中安装了 'mocha'。 - Yeoman 生成器 (Yeoman generator):调试 yeoman 生成器。代码片段会要求您指定生成器的名称。请确保您的项目已在其
node_modules
文件夹中安装了 'yo',并且您的生成项目已通过在项目文件夹中运行npm link
进行调试安装。 - Gulp 任务 (Gulp task):调试 gulp 任务。请确保您的项目已在其
node_modules
文件夹中安装了 'gulp'。 - Electron 主进程 (Electron Main):调试 Electron 应用程序的主 Node.js 进程。该代码片段假定 Electron 可执行文件已安装在工作区的
node_modules/.bin
目录中。
Node 控制台
默认情况下,Node.js 调试会话在 VS Code 内部调试控制台中启动目标。由于调试控制台不支持需要从控制台读取输入的程序,因此您可以通过将启动配置中的 console
属性分别设置为 externalTerminal
或 integratedTerminal
来启用外部终端或使用 VS Code 集成终端。默认值为 internalConsole
。
在外部终端中,您可以通过 terminal.external.windowsExec
、terminal.external.osxExec
和 terminal.external.linuxExec
设置来配置要使用的终端程序。
对“npm”和其他工具的启动配置支持
您可以直接从启动配置中使用 'npm' 脚本或其他任务运行器工具,而不是直接用 node 启动 Node.js 程序。
- 您可以将 PATH 上可用的任何程序(例如 'npm'、'mocha'、'gulp' 等)用于
runtimeExecutable
属性,并通过runtimeArgs
传递参数。 - 如果您的 npm 脚本或其他工具隐式指定了要启动的程序,则无需设置
program
属性。
我们来看一个 'npm' 示例。如果您的 package.json
有一个 'debug' 脚本,例如:
"scripts": {
"debug": "node myProgram.js"
},
相应的启动配置将如下所示:
{
"name": "Launch via npm",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "npm",
"runtimeArgs": ["run-script", "debug"]
}
多版本支持
如果您正在使用 'nvm' (或 'nvm-windows') 或 'nvs' 来管理您的 Node.js 版本,则可以在启动配置中指定 runtimeVersion
属性以选择特定版本的 Node.js:
{
"type": "node",
"request": "launch",
"name": "Launch test",
"runtimeVersion": "14",
"program": "${workspaceFolder}/test.js"
}
如果您正在使用 'nvs' 来管理您的 Node.js 版本,则可以使用 runtimeVersion
属性来选择特定版本、架构和风味的 Node.js,例如:
{
"type": "node",
"request": "launch",
"name": "Launch test",
"runtimeVersion": "chackracore/8.9.4/x64",
"program": "${workspaceFolder}/test.js"
}
请确保您已安装要与 runtimeVersion
属性一起使用的 Node.js 版本,因为此功能不会自动下载和安装版本。例如,如果您计划在启动配置中添加 "runtimeVersion": "7.10.1"
,则必须从集成终端运行类似 nvm install 7.10.1
或 nvs add 7.10.1
的命令。
如果您省略次版本和补丁版本,例如 "runtimeVersion": "14"
,则将使用您系统上安装的最新 14.x.y
版本。
从外部文件加载环境变量
VS Code Node 调试器支持从文件加载环境变量并将其传递给 Node.js 运行时。要使用此功能,请在您的启动配置中添加一个属性 envFile
并指定包含环境变量的文件的绝对路径:
//...
"envFile": "${workspaceFolder}/.env",
"env": { "USER": "john doe" }
//...
在 env
字典中指定的任何环境变量都将覆盖从文件加载的变量。
以下是一个 .env
文件的示例:
USER=doe
PASSWORD=abc123
# a comment
# an empty value:
empty=
# new lines expanded in quoted strings:
lines="foo\nbar"
附加到 Node.js
如果您想将 VS Code 调试器附加到外部 Node.js 程序,请按如下方式启动 Node.js:
node --inspect program.js
或者,如果程序不应立即开始运行,而必须等待调试器附加:
node --inspect-brk program.js
将调试器附加到程序的选项
- 打开一个“进程选择器”,列出所有可能的候选进程并让您选择一个,或者
- 创建一个明确指定所有配置选项的“附加”配置,然后按 F5。
让我们详细了解这些选项:
附加到 Node 进程操作
来自命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 的附加到 Node 进程 (Attach to Node Process) 命令会打开一个快速选择菜单,其中列出了所有可用于 Node.js 调试器的潜在进程:
选择器中列出的各个进程显示调试端口和进程 ID。一旦您在该列表中选择了您的 Node.js 进程,Node.js 调试器将尝试附加到它。
除了 Node.js 进程,选择器还显示了使用各种形式的 --inspect
参数启动的其他程序。这使得可以附加到 Electron 或 VS Code 的辅助进程。
设置“附加”配置
此选项需要更多工作,但与前两个选项相比,它允许您明确配置各种调试选项。
最简单的“附加”配置如下所示:
{
"name": "Attach to Process",
"type": "node",
"request": "attach",
"port": 9229
}
端口 9229
是 --inspect
和 --inspect-brk
选项的默认调试端口。要使用不同的端口(例如 12345
),请像这样将其添加到选项中:--inspect=12345
和 --inspect-brk=12345
,并更改启动配置中的 port
属性以匹配。
要附加到未在调试模式下启动的 Node.js 进程,可以通过将 Node.js 进程的进程 ID 指定为字符串来完成此操作:
{
"name": "Attach to Process",
"type": "node",
"request": "attach",
"processId": "53426"
}
为了避免在启动配置中重复输入新的进程 ID,Node 调试支持一个命令变量 PickProcess
,它将打开(如上所示的)进程选择器。
使用 PickProcess
变量,启动配置如下所示:
{
"name": "Attach to Process",
"type": "node",
"request": "attach",
"processId": "${command:PickProcess}"
}
停止调试
使用调试:停止 (Debug: Stop) 操作(在调试工具栏中或通过命令面板可用)停止调试会话。
如果调试会话是在“附加”模式下启动的(并且调试工具栏中的红色终止按钮显示一个叠加的“插头”图标),按下停止会断开 Node.js 调试器与被调试程序的连接,被调试程序随后继续执行。
如果调试会话处于“启动”模式,按下停止会执行以下操作:
-
首次按下停止时,会通过发送
SIGINT
信号请求被调试程序优雅地关闭。被调试程序可以自由拦截此信号,并根据需要清理任何内容,然后关闭。如果该关闭代码中没有断点(或问题),被调试程序和调试会话将终止。 -
但是,如果调试器在关闭代码中命中断点,或者被调试程序本身未能正确终止,则调试会话将不会结束。在这种情况下,再次按下停止将强制终止被调试程序及其子进程 (
SIGKILL
)。
如果您看到调试会话在按下红色停止按钮时没有结束,请再次按下该按钮以强制关闭被调试程序。
在 Windows 上,按下停止会强制终止被调试程序及其子进程。
源映射
VS Code 的 JavaScript 调试器支持源映射,有助于调试转译后的语言,例如 TypeScript 或缩小/混淆的 JavaScript。借助源映射,可以单步调试或在原始源文件中设置断点。如果原始源文件不存在源映射,或者源映射损坏且无法成功地在源文件和生成的 JavaScript 之间进行映射,则断点将显示为未验证状态(灰色空心圆圈)。
默认设置为 true
的 sourceMaps
属性控制源映射功能。调试器总是尝试使用源映射(如果能找到),因此,您甚至可以使用 program
属性指定一个源文件(例如 app.ts)。如果由于某种原因需要禁用源映射,可以将 sourceMaps
属性设置为 false
。
工具配置
由于源映射并非总是自动创建,您应确保配置您的转译器以创建它们。例如:
TypeScript
对于 TypeScript,您可以通过向 tsc
传递 --sourceMap
,或在 tsconfig.json 文件中添加 "sourceMap": true
来启用源映射。
tsc --sourceMap --outDir bin app.ts
Babel
对于 Babel,您需要将 sourceMaps 选项设置为 true
,或者在编译代码时传递 --source-maps
选项。
npx babel script.js --out-file script-compiled.js --source-maps
Webpack
Webpack 有许多源映射选项。我们建议在您的 webpack.config.js
中将属性 devtool: "source-map"
设置为获得最佳结果,尽管您可以尝试其他设置,但它们可能会导致构建速度变慢。
此外,如果您在 webpack 中有额外的编译步骤,例如使用 TypeScript 加载器,您还需要确保这些步骤也设置为生成源映射。否则,webpack 生成的源映射将映射回加载器编译的代码,而不是真实的源文件。
源映射发现
默认情况下,VS Code 会在您的整个工作区(不包括 node_modules
)中搜索源映射。在大型工作区中,此搜索可能会很慢。您可以通过在 launch.json
中设置 outFiles
属性来配置 VS Code 搜索源映射的位置。例如,此配置将仅发现 bin
文件夹中 .js
文件的源映射:
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch TypeScript",
"type": "node",
"request": "launch",
"program": "app.ts",
"outFiles": ["${workspaceFolder}/bin/**/*.js"]
}
]
}
请注意,outFiles
应该匹配您的 JavaScript 文件,而不是源映射文件(源映射文件可能以 .map
结尾而不是 .js
)。
源映射解析
默认情况下,只有 outFiles
中的源映射会被解析。此行为旨在防止依赖项干扰您设置的断点。例如,如果您有一个文件 src/index.ts
,并且某个依赖项的源映射引用了 webpack:///./src/index.ts
,则这会错误地解析到您的源文件,并可能导致意想不到的结果。
您可以通过设置 resolveSourceMapLocations
选项来配置此行为。如果设置为 null
,则每个源映射都将被解析。例如,此配置将额外允许解析 node_modules/some-dependency
中的源映射:
"resolveSourceMapLocations": [
"out/**/*.js",
"node_modules/some-dependency/**/*.js",
]
智能步进
在启动配置中将 smartStep
属性设置为 true
,VS Code 将在调试器中单步执行代码时自动跳过“不相关的代码”。“不相关的代码”是指由转译过程生成但未被源映射覆盖的代码,因此它不会映射回原始源。当您在调试器中单步执行源代码时,此代码会阻碍您,因为它会使调试器在原始源代码和您不感兴趣的生成代码之间切换。smartStep
将自动跳过未被源映射覆盖的代码,直到它再次到达被源映射覆盖的位置。
智能步进对于 TypeScript 中的 async/await 下编译等情况特别有用,因为编译器会注入未被源映射覆盖的辅助代码。
smartStep
功能仅适用于从源文件生成并因此具有源映射的 JavaScript 代码。对于没有源文件的 JavaScript,智能步进选项无效。
JavaScript 源映射提示
使用源映射进行调试时的一个常见问题是,您设置断点后,它会变成灰色。如果您将鼠标悬停在上面,您会看到消息:"Breakpoint ignored because generated code not found (source map problem?)"
(断点被忽略,因为未找到生成的代码(源映射问题?))。现在怎么办?有许多问题可能导致这种情况。首先,快速解释一下 Node 调试适配器如何处理源映射。
当您在 app.ts
中设置断点时,调试适配器必须找出 app.js
的路径,这是您的 TypeScript 文件的转译版本,也是实际在 Node 中运行的文件。但是,从 .ts
文件开始,没有直接的方法可以找到这个路径。相反,调试适配器使用 launch.json
中的 outFiles
属性来查找所有转译后的 .js
文件,并解析它们的源映射,其中包含其关联 .ts
文件的位置。
当您在 TypeScript 中启用源映射构建 app.ts
文件时,它会生成一个 app.js.map
文件,或者将源映射作为 base64 编码字符串以内联注释的形式包含在 app.js
文件的底部。为了找到与此映射关联的 .ts
文件,调试适配器会查看源映射中的两个属性:sources
和 sourceRoot
。sourceRoot
是可选的 - 如果存在,它会添加到 sources
(一个路径数组)中的每个路径前面。结果是一个 .ts
文件的绝对或相对路径数组。相对路径是相对于源映射解析的。
最后,调试适配器会在此生成的 .ts
文件列表中搜索 app.ts
的完整路径。如果找到匹配项,则表示它已找到用于将 app.ts
映射到 app.js
的源映射文件。如果没有匹配项,则无法绑定断点,并且断点会变成灰色。
当您的断点变为灰色时,可以尝试以下几种方法:
- 调试时,运行调试:诊断断点问题 (Debug: Diagnose Breakpoint Problems) 命令。此命令将从命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 调出一个工具,可以提供提示来帮助您解决任何问题。
- 您是否启用了源映射进行构建?请确保您的
.js
文件中存在.js.map
文件或内联源映射。 - 源映射中的
sourceRoot
和sources
属性是否正确?它们可以组合以获得.ts
文件的正确路径吗? - 您是否以不正确的路径大小写在 VS Code 中打开了文件夹?例如,可能会从命令行以
code FOO
的形式打开文件夹foo/
,在这种情况下,源映射可能无法正确解析。 - 尝试在 Stack Overflow 上搜索有关您特定设置的帮助,或在 GitHub 上提交问题。
- 尝试添加
debugger
语句。如果它在该处进入.ts
文件,但该位置的断点未绑定,这是在 GitHub 问题中很有用的信息。
覆盖源映射路径
调试器使用 sourceMapPathOverrides
来实现自定义的源映射到磁盘路径映射。大多数工具都有良好的默认设置,但在高级情况下您可能需要对其进行自定义。默认的路径覆盖是一个对象映射,如下所示:
{
'webpack:///./~/*': "${workspaceFolder}/node_modules/*",
'webpack:////*': '/*',
'webpack://@?:*/?:*/*': "${workspaceFolder}/*",
// and some more patterns...
}
这将源映射中的路径或 URL 从左侧映射到右侧。模式 ?:*
是一个非贪婪、非捕获匹配,而 *
是一个贪婪捕获匹配。然后,调试器会将右侧模式中对应的 *
替换为从源映射路径捕获的片段。例如,上述示例中的最后一个模式会将 webpack://@my/package/foo/bar
映射到 ${workspaceFolder}/foo/bar
。
请注意,对于浏览器调试,默认的 sourceMapPathOverrides
中使用 webRoot
代替 workspaceFolder
。
远程调试
注意: VS Code 现在具有通用的远程开发功能。使用远程开发扩展,在远程场景和容器中进行 Node.js 开发与在本地设置中进行 Node.js 开发没有区别。这是远程调试 Node.js 程序的推荐方式。请查看入门部分和远程教程以了解更多信息。
如果您无法使用任何远程开发扩展来调试您的 Node.js 程序,下面是关于如何从本地 VS Code 实例调试远程 Node.js 程序的指南。
Node.js 调试器支持远程调试,您可以附加到在不同机器或容器中运行的进程。通过 address
属性指定远程主机。例如:
{
"type": "node",
"request": "attach",
"name": "Attach to remote",
"address": "192.168.148.2", // <- remote address here
"port": 9229
}
默认情况下,VS Code 会将远程 Node.js 文件夹中被调试的源代码流式传输到本地 VS Code,并在只读编辑器中显示。您可以单步执行此代码,但无法修改它。如果您希望 VS Code 改为从您的工作区打开可编辑的源代码,您可以设置远程和本地位置之间的映射。localRoot
和 remoteRoot
属性可用于映射本地 VS Code 项目与(远程)Node.js 文件夹之间的路径。这甚至在同一系统或不同操作系统之间本地也有效。每当需要将代码路径从远程 Node.js 文件夹转换为本地 VS Code 路径时,remoteRoot
路径会被从路径中移除并替换为 localRoot
。对于反向转换,localRoot
路径会被替换为 remoteRoot
。
{
"type": "node",
"request": "attach",
"name": "Attach to remote",
"address": "TCP/IP address of process to be debugged",
"port": 9229,
"localRoot": "${workspaceFolder}",
"remoteRoot": "C:\\Users\\username\\project\\server"
}
访问已加载的脚本
如果您需要在不属于您的工作区,因此无法通过正常的 VS Code 文件浏览轻松定位和打开的脚本中设置断点,则可以通过运行和调试 (Run and Debug) 视图中的已加载脚本 (LOADED SCRIPTS) 视图来访问已加载的脚本:
当键入时启用过滤 (Enable Filter on Type) 开启时,已加载脚本 (LOADED SCRIPTS) 视图允许您通过键入脚本名称或过滤列表来快速选择脚本。
脚本加载到只读编辑器中,您可以在其中设置断点。这些断点在调试会话之间会被记住,但您只能在调试会话运行时访问脚本内容。
在源代码编辑时自动重启调试会话
启动配置的 restart
属性控制 Node.js 调试器在调试会话结束后是否自动重新启动。如果您使用 nodemon 在文件更改时重新启动 Node.js,此功能非常有用。将启动配置属性 restart
设置为 true
会使 Node 调试器在 Node.js 终止后自动尝试重新附加到 Node.js。
如果您已通过命令行使用 nodemon 启动了您的程序 server.js
,如下所示:
nodemon --inspect server.js
您可以使用以下启动配置将 VS Code 调试器附加到它:
{
"name": "Attach to node",
"type": "node",
"request": "attach",
"restart": true,
"port": 9229
}
或者,您可以使用启动配置直接通过 nodemon 启动您的程序 server.js
并附加 VS Code 调试器:
{
"name": "Launch server.js via nodemon",
"type": "node",
"request": "launch",
"runtimeExecutable": "nodemon",
"program": "${workspaceFolder}/server.js",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
提示:按下停止按钮会停止调试会话并断开与 Node.js 的连接,但 nodemon(和 Node.js)将继续运行。要停止 nodemon,您必须从命令行终止它(如果您使用如上所示的
integratedTerminal
,这很容易做到)。
提示:如果存在语法错误,nodemon 将无法成功启动 Node.js,直到错误得到修复。在这种情况下,VS Code 将继续尝试附加到 Node.js,但最终会放弃(10 秒后)。为避免此问题,您可以通过添加一个具有更大值(毫秒)的
timeout
属性来增加超时时间。
重启帧
Node 调试器支持在堆栈帧处重新启动执行。当您在源代码中发现问题并希望使用修改后的输入值重新运行一小段代码时,这会很有用。停止然后重新启动完整的调试会话可能会很耗时。在您使用设置值 (Set Value) 操作更改变量后,重启帧 (Restart Frame) 操作允许您重新进入当前函数。
重启帧 (Restart Frame) 不会回滚函数外部的状态变动,因此它可能不总是按预期工作。
断点
条件断点
条件断点是只有当表达式返回真值时才会暂停的断点。您可以通过右键单击行号旁边的边槽并选择“条件断点 (Conditional Breakpoint)”来创建一个。
日志点
有时您只想在代码命中某个位置时记录一条消息或值,而不是暂停。您可以使用日志点来实现此目的。日志点不会暂停,而是在命中时将消息记录到调试控制台。在 JavaScript 调试器中,您可以使用花括号将表达式插入到消息中,例如 current value is: {myVariable.property}
。
您可以通过右键单击行号旁边的边槽并选择“日志点 (Logpoint)”来创建一个。例如,这可能会记录类似 location is /usr/local
的内容:
命中计数断点
“命中计数条件”控制断点需要被命中多少次才会“中断”执行。您可以通过右键单击行号旁边的边槽,选择“条件断点 (Conditional Breakpoint)”,然后切换到“命中计数 (Hit Count)”来放置命中计数断点。
Node.js 调试器支持的命中计数语法是整数或以下运算符之一:<
、<=
、==
、>
、>=
、%
,后跟一个整数。
一些示例
>10
在命中 10 次后总是中断<3
仅在前两次命中时中断10
等同于>=10
%2
每隔一次命中时中断
触发断点
触发断点是一种在另一个断点命中后自动启用的断点。当代码中出现仅在特定先决条件发生后才出现的故障情况时,它们非常有用。
可以通过右键单击字形边距,选择 添加触发断点,然后选择哪个其他断点启用此断点来设置触发断点。
断点验证
出于性能原因,Node.js 会在首次访问时懒惰地解析 JavaScript 文件中的函数。因此,断点在 Node.js 尚未看到(解析)的源代码区域中不起作用。
由于此行为不利于调试,VS Code 会自动将 --nolazy
选项传递给 Node.js。这可以防止延迟解析,并确保在运行代码之前可以验证断点(因此它们不再“跳跃”)。
由于 --nolazy
选项可能会显著增加调试目标的启动时间,您可以通过将 --lazy
作为 runtimeArgs
属性传递来轻松选择退出。
这样做时,您会发现某些断点不会“粘”在请求的行上,而是“跳跃”到已解析代码中的下一个可能行。为避免混淆,VS Code 始终在 Node.js 认为断点所在的位置显示断点。在断点 (BREAKPOINTS) 部分,这些断点显示时,请求行号和实际行号之间有一个箭头:
此断点验证发生在会话启动并将断点注册到 Node.js 时,或会话已运行并设置新断点时。在这种情况下,断点可能会“跳跃”到不同的位置。在 Node.js 解析了所有代码(例如,通过运行它)之后,可以使用断点 (BREAKPOINTS) 部分标题中的重新应用 (Reapply) 按钮轻松地将断点重新应用于请求的位置。这应该会使断点“跳回”到请求的位置。
跳过不相关的代码
VS Code Node.js 调试具有一个功能,可以避免您不想单步执行的源代码(也称为“仅我的代码”)。此功能可以通过您的启动配置中的 skipFiles
属性启用。skipFiles
是用于跳过的脚本路径的 glob 模式数组。
例如,使用:
"skipFiles": [
"${workspaceFolder}/node_modules/**/*.js",
"${workspaceFolder}/lib/**/*.js"
]
您项目中的 node_modules
和 lib
文件夹中的所有代码都将被跳过。skipFiles
也适用于调用 console.log
和类似方法时显示的位置:堆栈中第一个未跳过的位置将显示在调试控制台的输出旁边。
Node.js 的内置核心模块可以通过 glob 模式中的“魔术名称” <node_internals>
来引用。以下示例跳过所有内部模块:
"skipFiles": [
"<node_internals>/**/*.js"
]
精确的“跳过”规则如下:
- 如果您单步进入一个跳过的文件,您不会在那里停止 - 您将在不在跳过文件中的下一条执行行停止。
- 如果您已设置选项在抛出异常时中断,则您不会在从跳过文件抛出的异常处中断,除非它们冒泡到非跳过文件中。
- 如果您在跳过文件中设置断点,您将会在该断点处停止,并且可以单步执行它,直到您跳出该文件,此时将恢复正常的跳过行为。
- 来自跳过文件内部的控制台消息的位置将显示为调用堆栈中第一个未跳过的位置。
在“调用堆栈 (CALL STACK)”视图中,跳过的源以“变暗”样式显示:
将鼠标悬停在变暗的条目上会解释为什么堆栈帧会变暗。
调用堆栈上的一个上下文菜单项,切换跳过此文件 (Toggle skipping this file) 使您可以在运行时轻松跳过文件,而无需将其添加到您的启动配置中。此选项仅在当前调试会话中有效。您也可以使用它来停止跳过在启动配置中由 skipFiles
选项跳过的文件。
注意:
legacy
协议调试器支持负的 glob 模式,但它们必须跟随一个正模式:正模式会添加到跳过文件的集合中,而负模式则会从该集合中减去。
在以下(仅限 legacy
协议)示例中,除了“math”模块外,所有其他模块都被跳过:
"skipFiles": [
"${workspaceFolder}/node_modules/**/*.js",
"!${workspaceFolder}/node_modules/math/**/*.js"
]
注意:
legacy
协议调试器必须模拟skipFiles
功能,因为 V8 调试器协议 (V8 Debugger Protocol) 不原生支持它。这可能会导致单步执行性能缓慢。
调试 WebAssembly
如果代码中包含 DWARF 调试信息,JavaScript 调试器可以调试编译成 WebAssembly 的代码。许多工具链支持发出此信息:
- 使用 Emscripten 的 C/C++:使用
-g
标志编译以发出调试信息。 - Zig:DWARF 信息在“调试 (Debug)”构建模式下自动发出。
- Rust:Rust 发出 DWARF 调试信息。然而,wasm-pack 尚未在构建过程中保留它。因此,wasm-bindgen/wasm-pack 常用库的用户应使用两个命令手动构建:
cargo install wasm-bindgen-cli
一次以安装必要的命令行工具。cargo build --target wasm32-unknown-unknown
以构建您的库。wasm-bindgen --keep-debug --out-dir pkg ./target/wasm32-unknown-unknown/debug/<library-name>.wasm <extra-arguments>
以生成 WebAssembly 绑定,将<library-name>
替换为您 Cargo.toml 中的名称并根据需要配置<extra-arguments>
。
构建代码后,您需要安装 WebAssembly DWARF Debugging 扩展。此扩展作为一个单独的扩展发布,以保持 VS Code 核心的“精简”。安装后,重新启动任何活动的调试会话,然后本地代码应在调试器中进行映射!您应该会在已加载源 (Loaded Sources) 视图中看到您的源代码,并且断点应该能正常工作。
在下图中,调试器停止在 C++ 源代码中创建 Mandelbrot 分形的断点处。调用堆栈可见,其中包含从 JavaScript 代码到 WebAssembly 再到映射的 C++ 代码的帧。您还可以看到 C++ 代码中的变量,以及对与 int32 height
变量相关的内存的编辑。
虽然接近对等,但调试 WebAssembly 与普通 JavaScript 略有不同:
- 变量 (Variables) 视图中的变量不能直接编辑。但是,您可以通过选择变量旁边的查看二进制数据 (View Binary Data) 操作来编辑其关联的内存。
- 调试控制台 (Debug Console) 和监视 (Watch) 视图中的基本表达式求值由 lldb-eval 提供。这与普通 JavaScript 表达式不同。
- 未映射到源代码的位置将以反汇编的 WebAssembly 文本格式显示。对于 WebAssembly,命令禁用源映射步进 (Disable Source Map Stepping) 将使调试器仅在反汇编的代码中单步执行。
VS Code 的 WebAssembly 调试是基于 Chromium 作者的 C/C++ Debugging Extension 构建的。
支持的类 Node 运行时
当前 VS Code JavaScript 调试器支持 Node 8.x 及更高版本、最新 Chrome 版本和最新 Edge 版本(通过 msedge
启动类型)。
后续步骤
如果您还没有阅读 Node.js 部分,请查看:
- Node.js - 带有示例应用程序的端到端 Node 场景
要查看 VS Code 调试基础知识的教程,请观看此视频:
要了解 VS Code 的任务运行支持,请访问:
- 任务 - 使用 Gulp、Grunt 和 Jake 运行任务。显示错误和警告
要编写自己的调试器扩展,请访问:
- 调试器扩展 - 从模拟示例开始创建 VS Code 调试扩展的步骤
常见问题
如果我使用符号链接,可以调试吗?
是的,如果您在项目中为文件夹创建了符号链接(例如使用 npm link
),您可以通过告诉 Node.js 运行时保留符号链接路径来调试符号链接的源。在您的启动配置的 runtimeArgs
属性中使用 node.exe 的 --preserve-symlinks
开关。runtimeArgs
是一个字符串数组,会传递给调试会话的运行时可执行文件,默认为 node.exe。
{
"runtimeArgs": ["--preserve-symlinks"]
}
如果您的主脚本位于符号链接路径内,那么您还需要添加 "--preserve-symlinks-main"
选项。此选项仅在 Node 10+ 中可用。
如何调试 ECMAScript 模块?
如果您使用 esm 或向 Node.js 传递 --experimental-modules
以使用 ECMAScript 模块,可以通过 launch.json
的 runtimeArgs
属性传递这些选项:
"runtimeArgs": ["--experimental-modules"]
- 使用 Node v8.5.0+ 中实验性的 ECMAScript 模块支持"runtimeArgs": ["-r", "esm"]
- 使用esm ES 模块加载器(["-r esm"]
如果没有逗号将无法工作)
如何设置 NODE_OPTIONS?
调试器使用特殊的 NODE_OPTIONS
环境变量来设置应用程序的调试,覆盖它将阻止调试正常工作。您应该改为附加到它,而不是覆盖它。例如,一个 .bashrc
文件可能包含类似这样的内容:
export NODE_OPTIONS="$NODE_OPTIONS --some-other-option=here"