现已推出!阅读有关 11 月的新功能和修复的信息。

VS Code 中的 Node.js 调试

Visual Studio Code 编辑器内置了对 Node.js 运行时的调试支持,并且可以调试 JavaScript、TypeScript 以及许多其他被转译为 JavaScript 的语言。使用 VS Code 设置 Node.js 调试项目非常简单,它提供了适当的启动配置默认值和代码片段。

在 VS Code 中,你可以通过几种方式调试你的 Node.js 程序

自动附加

如果启用了 自动附加 功能,则 Node 调试器会自动附加到从 VS Code 集成终端启动的某些 Node.js 进程。要启用此功能,请使用命令面板中的 切换自动附加 命令(⇧⌘P(Windows,Linux Ctrl+Shift+P),或者如果它已经激活,则使用 自动附加 状态栏项目。

自动附加有三种模式,你可以在生成的快速选择中以及通过 debug.javascript.autoAttachFilter 设置进行选择

  • smart - 如果你在 node_modules 文件夹外部执行脚本,或者使用常见的“运行器”脚本(如 mocha 或 ts-node),则将调试该进程。你可以使用 自动附加智能模式 设置(debug.javascript.autoAttachSmartPattern)配置“运行器”脚本允许列表。
  • always - 将调试在集成终端中启动的所有 Node.js 进程。
  • onlyWithFlag - 仅调试使用 --inspect--inspect-brk 标志启动的进程。

启用 自动附加 后,你需要通过单击终端右上角的 ⚠ 图标或仅创建新终端来重启终端。然后,调试器应在一秒钟内附加到你的程序

Auto Attach

当自动附加开启时,自动附加 项将出现在 VS Code 窗口底部的状态栏中。单击它可以更改自动附加模式,或暂时将其关闭。如果你正在运行一些不需要调试的一次性程序,但你不想完全禁用该功能,则暂时关闭自动附加功能非常有用。

其他配置

其他启动配置属性

你可以在 debug.javascript.terminalOptions 设置中将 通常在 launch.json 中找到的其他属性应用于自动附加。例如,要将 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-nodemochaava 等。如果这些设置不起作用,你可以修改此列表。例如,要排除 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 进程。你可以通过从命令面板运行 调试: 创建 JavaScript 调试终端 命令(kbs(workbench.action.showCommands)),或从终端切换器下拉列表中选择 创建 JavaScript 调试终端 来创建调试终端。

Create Debug Terminal

其他配置

其他启动配置属性

你可以在 debug.javascript.terminalOptions 设置中将 通常在 launch.json 中找到的其他属性应用于调试终端。例如,要将 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 选项文档中查看完整的选项集。

以下属性在类型为 launchattach 的启动配置中受支持

  • 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 - 用于启动程序的控制台 (internalConsoleintegratedTerminalexternalTerminal)。请参阅下面的 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 configuration snippets for Node.js

您还可以使用 launch.json 编辑器窗口右下角的“添加配置...”按钮来调出代码段。

Add Configuration button

可以使用以下代码段

  • 启动程序:以调试模式启动 Node.js 程序。
  • 通过 npm 启动:通过 npm“debug”脚本启动 Node.js 程序。如果您的 package.json 中定义了 npm 调试脚本,则可以使用启动配置中的 npm 调试脚本。npm 脚本中使用的调试端口必须与代码段中指定的端口相对应。
  • 附加:附加到本地运行的 Node.js 程序的调试端口。确保要调试的 Node.js 程序已在调试模式下启动,并且使用的调试端口与代码段中指定的端口相同。
  • 附加到远程程序:附加到由 address 属性指定的主机上运行的 Node.js 程序的调试端口。确保要调试的 Node.js 程序已在调试模式下启动,并且使用的调试端口与代码段中指定的端口相同。为了帮助 VS Code 在您的工作区和远程主机的文件系统之间映射源文件,请确保为 localRootremoteRoot 属性指定正确的路径。
  • 按进程 ID 附加:打开进程选择器以选择要调试的 node 或 gulp 进程。使用此启动配置,您甚至可以附加到未在调试模式下启动的 node 或 gulp 进程。
  • Nodemon 设置:使用 nodemon 在 JavaScript 源发生更改时自动重新启动调试会话。确保您已全局安装 nodemon。请注意,终止调试会话只会终止要调试的程序,而不会终止 nodemon 本身。要终止 nodemon,请在集成终端中按 Ctrl+C
  • Mocha 测试:调试项目 test 文件夹中的 mocha 测试。确保您的项目在其 node_modules 文件夹中安装了“mocha”。
  • Yeoman 生成器:调试 yeoman 生成器。代码段会要求您指定生成器的名称。确保您的项目在其 node_modules 文件夹中安装了“yo”,并且您生成的项目已通过在项目文件夹中运行 npm link 安装以进行调试。
  • Gulp 任务:调试 gulp 任务。确保您的项目在其 node_modules 文件夹中安装了“gulp”。
  • Electron Main:调试 Electron 应用程序的主 Node.js 进程。该代码段假定 Electron 可执行文件已安装在工作区的 node_modules/.bin 目录中。

Node 控制台

默认情况下,Node.js 调试会话会在 VS Code 内部调试控制台中启动目标。由于调试控制台不支持需要从控制台读取输入的程序,因此您可以通过将启动配置中的 console 属性设置为 externalTerminalintegratedTerminal 来启用外部终端或使用 VS Code 集成终端。默认值为 internalConsole

在外部终端中,您可以通过 terminal.external.windowsExecterminal.external.osxExecterminal.external.linuxExec 设置来配置要使用的终端程序。

对“npm”和其他工具的启动配置支持

您可以直接从启动配置中使用“npm”脚本或其他任务运行器工具,而不是直接使用 node 启动 Node.js 程序。

  • 您可以对 runtimeExecutable 属性使用 PATH 上可用的任何程序(例如 'npm'、'mocha'、'gulp' 等),并且可以通过 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”)来管理 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.1nvs 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 进程操作

命令面板中的“附加到 Node 进程”命令 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 将打开一个快速选择菜单,其中列出了 Node.js 调试器可用的所有潜在进程。

Node.js Process picker

选择器中列出的各个进程显示调试端口和进程 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}"
}

停止调试

使用“调试:停止”操作(可在调试工具栏中或通过 命令面板 获得)会停止调试会话。

如果调试会话是在“附加”模式下启动的(调试工具栏中的红色终止按钮显示一个叠加的“插头”),则按 停止 会将 Node.js 调试器从调试对象断开连接,然后调试对象继续执行。

如果调试会话处于“启动”模式,则按 停止 会执行以下操作:

  1. 首次按 停止 时,会通过发送 SIGINT 信号请求调试对象正常关闭。调试对象可以自由地拦截此信号并清理任何必要的内容,然后关闭。如果该关闭代码中没有断点(或问题),则调试对象和调试会话将终止。

  2. 但是,如果调试器在关闭代码中遇到断点,或者调试对象本身无法正确终止,则调试会话将不会结束。在这种情况下,再次按 停止 将强制终止调试对象及其子进程 (SIGKILL)。

如果您看到按红色 停止 按钮时调试会话没有结束,请再次按该按钮以强制关闭调试对象。

在 Windows 上,按 停止 会强制终止调试对象及其子进程。

源映射

VS Code 的 JavaScript 调试器支持源代码映射,这有助于调试转换的语言,例如 TypeScript 或缩小/丑化的 JavaScript。使用源代码映射,可以在原始源代码中单步执行或设置断点。如果原始源代码不存在源代码映射,或者源代码映射已损坏且无法成功地在源代码和生成的 JavaScript 之间进行映射,则断点会显示为未验证(灰色空心圆)。

默认为 truesourceMaps 属性控制源代码映射功能。调试器始终尝试使用源代码映射(如果可以找到),因此,您甚至可以使用 program 属性指定源文件(例如,app.ts)。如果由于某种原因需要禁用源代码映射,则可以将 sourceMaps 属性设置为 false

工具配置

由于并非始终会自动创建源代码映射,因此您应确保配置转换器以创建它们。例如:

TypeScript

对于 TypeScript,您可以通过将 --sourceMap 传递给 tsc 或在 tsconfig.json 文件中添加 "sourceMap": true 来启用 sourcemaps。

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 加载器),您还需要确保这些步骤设置为生成 sourcemaps。否则,webpack 生成的 sourcemaps 将映射回加载器中的已编译代码,而不是实际的源代码。

源代码映射发现

默认情况下,VS Code 将在整个工作区中搜索 sourcemaps,但不包括 node_modules。在大型工作区中,此搜索可能会很慢。您可以通过在 launch.json 中设置 outFiles 属性来配置 VS Code 搜索源代码映射的位置。例如,此配置将仅发现 bin 文件夹中 .js 文件的 sourcemaps:

{
  "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 文件,调试适配器会查看源代码映射中的两个属性 sourcessourceRootsourceRoot 是可选的 - 如果存在,它会附加到 sources 中的每个路径,后者是一个路径数组。结果是一个 .ts 文件的绝对或相对路径数组。相对路径相对于源代码映射解析。

最后,调试适配器在此生成的 .ts 文件列表中搜索 app.ts 的完整路径。如果有匹配项,则它已找到在将 app.ts 映射到 app.js 时要使用的源代码映射文件。如果没有匹配项,则它无法绑定断点,并且它将变为灰色。

以下是一些当您的断点变为灰色时要尝试的操作:

  • 调试时,运行 调试:诊断断点问题 命令。此命令将弹出一个工具,该工具可以提供提示来帮助您从命令面板中解决任何问题(⇧⌘P (Windows, Linux Ctrl+Shift+P))。
  • 您是否在启用了源代码映射的情况下构建?确保存在 .js.map 文件或 .js 文件中的内联源代码映射。
  • 源代码映射中的 sourceRootsources 属性是否正确?它们是否可以组合以获得 .ts 文件的正确路径?
  • 您是否在 VS Code 中打开了大小写不正确的文件夹?可以从命令行打开文件夹 foo/,例如 code FOO,在这种情况下,源代码映射可能无法正确解析。
  • 尝试在 Stack Overflow 上搜索有关您的特定设置的帮助,或在 GitHub 上提交问题。
  • 尝试添加 debugger 语句。如果它在那里中断到 .ts 文件,但该位置的断点没有绑定,这是在 GitHub 问题中包含的有用信息。

覆盖源代码映射路径

调试器使用 sourceMapPathOverrides 来实现自定义的 sourcemap 到磁盘的路径映射。大多数工具都具有良好的默认设置,但在高级情况下,您可能需要自定义它。默认的路径覆盖是一个对象映射,如下所示:

{
  '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 打开工作区中可编辑的源代码,则可以在远程位置和本地位置之间设置映射。可以使用 localRootremoteRoot 属性来映射本地 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 文件浏览轻松定位和打开的脚本中设置断点,您可以通过运行和调试视图中的 已加载脚本 视图访问已加载的脚本。

Loaded Scripts Explorer

启用键入时筛选 开启时,已加载脚本 视图允许您通过键入脚本名称快速选择脚本,或筛选列表。

脚本会加载到只读编辑器中,您可以在其中设置断点。这些断点会在调试会话中保留,但您只能在调试会话运行时访问脚本内容。

在编辑源时自动重启调试会话

启动配置的 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 调试器支持在堆栈帧处重新启动执行。当您在源代码中发现问题,并希望使用修改后的输入值重新运行一小部分代码时,此功能非常有用。停止然后重新启动整个调试会话可能很耗时。重新启动帧 操作允许您在使用 设置值 操作更改变量后重新进入当前函数。

Restart frame

重新启动帧 不会回滚函数外部状态的更改,因此它可能并不总是按预期工作。

断点

条件断点

条件断点是指仅当表达式返回真值时才暂停的断点。您可以通过在行号旁边的槽中单击右键并选择“条件断点”来创建一个条件断点

Conditional breakpoint

日志点

有时,您只想在代码到达特定位置时记录消息或值,而不是暂停。您可以使用日志点来做到这一点。日志点不会暂停,而是在命中时将消息记录到“调试控制台”。在 JavaScript 调试器中,您可以使用花括号将表达式插入到消息中,例如 当前值是:{myVariable.property}

您可以通过在行号旁边的槽中单击右键并选择“日志点”来创建一个日志点。例如,这可能会记录类似于 位置是 /usr/local 的内容

Logpoint

命中计数断点

“命中计数条件”控制断点在“中断”执行之前需要被命中的次数。您可以通过在行号旁边的槽中单击右键,选择“条件断点”,然后切换到“命中计数”来放置命中计数断点。

Hit count breakpoint

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 View

断点验证发生在会话开始且断点在 Node.js 中注册时,或会话已在运行时且设置了新断点时。在这种情况下,断点可能会“跳转”到不同的位置。在 Node.js 解析完所有代码(例如,通过运行代码)之后,可以使用 断点 部分标题中的 重新应用 按钮轻松地将断点重新应用于请求的位置。这应该使断点“跳回”到请求的位置。

Breakpoint Actions

跳过不感兴趣的代码

VS Code Node.js 调试具有一项功能,可以避免您不想单步执行的源代码(也称为“仅我的代码”)。可以使用启动配置中的 skipFiles 属性启用此功能。skipFiles 是要跳过的脚本路径的 glob 模式 数组。

例如,使用

  "skipFiles": [
    "${workspaceFolder}/node_modules/**/*.js",
    "${workspaceFolder}/lib/**/*.js"
  ]

将跳过项目中的 node_moduleslib 文件夹中的所有代码。当调用 console.log 和类似方法时,skipFiles 也适用于显示的位置:堆栈中第一个未跳过的位置将显示在“调试控制台”中输出的旁边。

Node.js 的内置 核心模块 可以通过 glob 模式 中的“魔法名称”<node_internals> 来引用。以下示例跳过所有内部模块

  "skipFiles": [
     "<node_internals>/**/*.js"
   ]

确切的“跳过”规则如下

  • 如果您单步进入一个跳过的文件,您不会停留在那里 - 您将在下一个未跳过文件中执行的行上停止。
  • 如果您设置了在抛出异常时中断的选项,那么您不会在从跳过的文件中抛出的异常中断,除非它们冒泡到未跳过的文件中。
  • 如果您在跳过的文件中设置断点,您将在该断点处停止,并且您可以单步执行该断点,直到您单步退出它,届时将恢复正常的跳过行为。
  • 来自跳过文件内部的控制台消息的位置将显示为调用堆栈中第一个未跳过的位置。

跳过的源在“调用堆栈”视图中以“灰色”样式显示

Skipped source is dimmed in call stack view

将鼠标悬停在灰色的条目上会解释为什么堆栈帧是灰色的。

调用堆栈上的上下文菜单项 切换跳过此文件 使您能够在运行时轻松跳过文件,而无需将其添加到启动配置中。此选项仅在当前调试会话中保留。您也可以使用它来停止跳过启动配置中 skipFiles 选项跳过的文件。

注意: legacy 协议调试器支持负 glob 模式,但它们必须 跟随 正模式:正模式会添加到跳过文件集中,而负模式会从该集中减去。

在以下(仅 legacy 协议)示例中,除了“math”模块之外的所有模块都被跳过

"skipFiles": [
    "${workspaceFolder}/node_modules/**/*.js",
    "!${workspaceFolder}/node_modules/math/**/*.js"
]

注意: legacy 协议调试器必须模拟 skipFiles 功能,因为 V8 调试器协议 本身不支持它。这可能会导致单步执行性能缓慢。

调试 WebAssembly

如果 JavaScript 调试器包含 DWARF 调试信息,则可以调试编译为 WebAssembly 的代码。许多工具链都支持发出此信息

  • 使用 Emscripten 的 C/C++:使用 -g 标志进行编译以发出调试信息。
  • Zig:DWARF 信息在“Debug”构建模式下会自动发出。
  • Rust:Rust 会发出 DWARF 调试信息。但是,wasm-pack 尚未 在构建期间保留它。因此,wasm-bindgen/wasm-pack 库的普通用户应该使用两个命令手动构建,而不是运行 wasm-pack build
    1. cargo install wasm-bindgen-cli 一次安装必要的命令行工具。
    2. cargo build --target wasm32-unknown-unknown 构建您的库。
    3. 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 调试 扩展。此扩展作为单独的扩展发布,以保持 VS Code 核心的“精简”。安装后,重新启动任何活动的调试会话,本地代码应该就会在调试器中映射!您应该在 已加载源 视图中看到源代码,并且断点应该可以工作。

在下面的图像中,调试器停止在 C++ 源代码中的断点处,该代码创建了 Mandelbrot 分形。调用堆栈是可见的,其中包含来自 JavaScript 代码、WebAssembly 代码和映射的 C++ 代码的帧。您还可以看到 C++ 代码中的变量,以及对与 int32 height 变量关联的内存的编辑。

Debugger stopped on a breakpoint in C++ source code

虽然接近对等,但调试 WebAssembly 与普通的 JavaScript 有些不同

  • 变量 视图中,变量不能直接编辑。但是,您可以选择变量旁边的 查看二进制数据 操作来编辑其关联的内存。
  • 调试控制台监视 视图中的基本表达式求值由 lldb-eval 提供。这与普通的 JavaScript 表达式不同。
  • 未映射到源代码的位置将以反汇编的 WebAssembly 文本格式显示。对于 WebAssembly,命令 禁用源代码映射单步调试 将使调试器仅在反汇编代码中单步执行。

VS Code 的 WebAssembly 调试是基于 Chromium 作者的 C/C++ 调试扩展 构建的。

支持的类 Node 运行时

当前 VS Code JavaScript 调试器支持 8.x 或更高版本的 Node,以及最新的 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 或将 --experimental-modules 传递给 Node.js 以使用 ECMAScript 模块,您可以通过 launch.jsonruntimeArgs 属性传递这些选项。

如何设置 NODE_OPTIONS?

调试器使用特殊的 NODE_OPTIONS 环境变量来设置与应用程序的调试,覆盖它会阻止调试正常工作。您应该追加到它,而不是覆盖它。例如,.bashrc 文件可能如下所示

export NODE_OPTIONS="$NODE_OPTIONS --some-other-option=here"