现已发布!阅读关于 12 月份的新功能和修复。

VS Code 中的 Node.js 调试

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

您可以通过几种方式在 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 进程。您可以通过从命令面板运行 **Debug: Create JavaScript Debug Terminal** 命令(kbs(workbench.action.showCommands)),或从终端选择器下拉菜单中选择 **Create JavaScript Debug Terminal** 来创建调试终端。

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 options 文档中查看所有选项。

以下属性支持在请求类型为 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 脚本中使用的调试端口必须与代码片段中指定的端口相对应。
  • 附加:附加到本地运行的 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 程序。

  • 您可以使用 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”)来管理您的 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.js 调试器支持从文件加载环境变量并将其传递给 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))中的 **Attach to Node Process** 命令会打开一个快速选择菜单,其中列出了 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}"
}

停止调试

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

如果调试会话是以“附加”模式启动的(并且调试工具栏中的红色终止按钮显示一个叠加的“插头”),按 **Stop** 会断开 Node.js 调试器与被调试程序的连接,然后被调试程序将继续执行。

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

  1. 首次按下 **Stop** 时,通过发送 SIGINT 信号来请求被调试程序优雅关闭。被调试程序可以拦截此信号并根据需要清理任何内容,然后关闭。如果没有断点(或问题)在该关闭代码中,被调试程序和调试会话将终止。

  2. 但是,如果调试器在关闭代码中命中了一个断点,或者被调试程序本身没有正确终止,那么调试会话将不会结束。在这种情况下,再次按下 **Stop** 将强制终止被调试程序及其子进程(SIGKILL)。

如果您发现按停止按钮(红色)时调试会话没有结束,请再次按下按钮以强制关闭被调试程序。

在 Windows 上,按下 **Stop** 会强制终止被调试程序及其子进程。

源映射

VS Code 的 JavaScript 调试器支持源映射,这有助于调试转译后的语言,例如 TypeScript 或已压缩/混淆的 JavaScript。使用源映射,可以单步执行或在原始源代码中设置断点。如果原始源代码没有源映射,或者源映射损坏且无法成功映射源与生成的 JavaScript 之间的关系,则断点将显示为未验证(灰色空心圆)。

sourceMaps 属性(默认为 true)控制源映射功能。调试器始终尝试使用源映射(如果能找到任何),因此,您甚至可以使用 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" 属性设置为“source-map”,以获得最佳结果,但您也可以尝试其他可能导致构建缓慢的设置。

此外,如果您的 webpack 中还有其他编译步骤,例如使用 TypeScript 加载器,您还需要确保这些步骤已设置为生成 sourcemaps。否则,Webpack 生成的 sourcemaps 将映射回加载器的编译代码,而不是实际的源文件。

源映射发现

默认情况下,VS Code 将搜索整个工作区(排除 node_modules)以查找 sourcemaps。在大型工作区中,此搜索可能很慢。您可以通过在 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 文件,要么会在 app.js 文件底部的注释中以 base64 编码字符串的形式内联源映射。为了找到与该映射相关的 .ts 文件,调试适配器会查看源映射中的两个属性:sourcessourceRootsourceRoot 是可选的;如果存在,它会添加到 sources(一个路径数组)中的每个路径前面。结果是 .ts 文件的绝对或相对路径。相对路径相对于源映射进行解析。

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

当断点变成灰色时,您可以尝试以下方法:

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

覆盖源映射路径

调试器使用 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 现在具有通用的 远程开发功能。使用 Remote Development 扩展,在远程场景和容器中进行 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 文件浏览轻松找到和打开它,则可以通过 **“运行和调试”** 视图中的 **“已加载脚本”** 视图访问已加载的脚本。

Loaded Scripts Explorer

当 **“启用按类型过滤”** 开启时,“已加载脚本”视图可让您通过输入脚本名称快速选择脚本或过滤列表。

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

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

启动配置的 `restart` 属性控制调试会话结束后 Node.js 调试器是否自动重新启动。如果您使用 nodemon 在文件更改时重新启动 Node.js,此功能非常有用。将启动配置属性 `restart` 设置为 `true` 会使 Node.js 调试器在 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

**重新启动帧** 不会回滚对函数外部状态的更改,因此可能无法始终按预期工作。

Breakpoints

条件断点

条件断点是在表达式返回真值时才暂停的断点。您可以右键单击行号旁边的装订区域并选择“条件断点”来创建一个。

Conditional breakpoint

日志点

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

您可以右键单击行号旁边的装订区域并选择“日志点”来创建一个。例如,这可能会记录类似 `location is /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_modules` 和 `lib` 文件夹中的所有代码都将被跳过。`skipFiles` 也适用于调用 `console.log` 和类似方法时显示的位置:堆栈中第一个未跳过的位置将显示在调试控制台的输出旁边。

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

  "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 调试器可以调试编译为 WebAssembly 的代码,前提是它包含 DWARF 调试信息。许多工具链都支持发出此信息:

  • C/C++ (Emscripten):使用 `-g` 标志进行编译以发出调试信息。
  • Zig:“Debug”构建模式会自动发出 DWARF 信息。
  • 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. 生成 WebAssembly 绑定:`wasm-bindgen --keep-debug --out-dir pkg ./target/wasm32-unknown-unknown/debug/.wasm `,将 `` 替换为您的 Cargo.toml 中的名称,并根据需要配置 ``。

构建完代码后,您需要安装 WebAssembly DWARF Debugging 扩展。这是一个单独的扩展,以保持 VS Code 核心的“精简”。安装后,重新启动任何活动的调试会话,此时本地代码应该已经在调试器中映射!您应该会在 **已加载源代码** 视图中看到您的源代码,并且断点应该可以正常工作。

在下面的图像中,调试器在 C++ 源代码的一个断点处停止,该断点创建了一个曼德勃罗集分形。调用堆栈可见,包含从 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++ Debugging Extension

支持的类 Node.js 运行时

当前的 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.json` 的 `runtimeArgs` 属性传递这些选项。

如何设置 NODE_OPTIONS?

调试器使用特殊的 `NODE_OPTIONS` 环境变量来设置调试与您的应用程序,覆盖它将导致调试无法正常工作。您应该在其后追加,而不是覆盖它。例如,`.bashrc` 文件可能包含类似以下内容:

export NODE_OPTIONS="$NODE_OPTIONS --some-other-option=here"
© . This site is unofficial and not affiliated with Microsoft.