在 VS Code 中進行 Node.js 偵錯

Visual Studio Code 編輯器內建了對 Node.js 執行時期的偵錯支援,並可偵錯 JavaScript、TypeScript 以及許多其他轉譯為 JavaScript 的語言。VS Code 提供適當的預設啟動設定和程式碼片段 (snippets),讓設定 Node.js 偵錯專案變得非常簡單。

您可以透過幾種方式在 VS Code 中偵錯您的 Node.js 程式:

自動附加 (Auto Attach)

如果啟用了自動附加 (Auto Attach) 功能,Node 偵錯器會自動附加至某些從 VS Code 整合終端機啟動的 Node.js 處理序。若要啟用此功能,請使用命令選擇區 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 中的切換自動附加 (Toggle Auto Attach) 命令;若已啟用,則使用狀態列上的自動附加項目。

自動附加有三種模式,您可以在隨後出現的「快速選擇 (Quick Pick)」選單中選擇,或透過 debug.javascript.autoAttachFilter 設定來選擇:

  • smart - 如果您在 node_modules 資料夾之外執行指令碼,或使用像 mocha 或 ts-node 這類常見的「執行器 (runner)」指令碼,該處理序就會被偵錯。您可以透過 Auto Attach Smart Pattern 設定 (debug.javascript.autoAttachSmartPattern) 來設定「執行器」指令碼的允許清單。
  • always - 在整合終端機中啟動的所有 Node.js 處理序都會被偵錯。
  • onlyWithFlag - 只有以 --inspect--inspect-brk 旗標啟動的處理序會被偵錯。

啟用自動附加後,您需要透過點擊終端機右上角的 ⚠ 圖示,或直接建立一個新的終端機來重新啟動它。接著,偵錯器應會在幾秒鐘內附加到您的程式。

Auto Attach

當自動附加開啟時,自動附加項目會出現在 VS Code 視窗底部的狀態列中。點擊它可以更改自動附加模式或暫時關閉它。如果您正在執行一些不需要偵錯的一次性程式,但又不想完全停用此功能,暫時關閉自動附加會很有用。

額外設定

其他啟動設定屬性

您可以將通常在 launch.json 中找到的其他屬性套用於 debug.javascript.terminalOptions 設定中的自動附加功能。例如,若要將 node 內部模組加入您的 skipFiles,您可以將以下內容加入您的使用者或工作區設定:

  "debug.javascript.terminalOptions": {
    "skipFiles": [
      "<node_internals>/**"
    ]
  },

自動附加智慧模式 (Auto Attach Smart Patterns)

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$ 會被常見的「程式碼執行器 (code runners)」清單取代,例如 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 處理序。您可以透過命令選擇區 (kbs(workbench.action.showCommands)) 執行 Debug: Create JavaScript Debug Terminal 命令,或從終端機切換下拉選單中選擇 Create JavaScript Debug Terminal 來建立偵錯終端機。

Create Debug Terminal

額外設定

其他啟動設定屬性

您可以將通常在 launch.json 中找到的其他屬性套用於 debug.javascript.terminalOptions 設定中的偵錯終端機。例如,若要將 node 內部模組加入您的 skipFiles,您可以將以下內容加入您的使用者或工作區設定:

"debug.javascript.terminalOptions": {
  "skipFiles": [
    "<node_internals>/**"
  ]
},

啟動設定 (Launch Configuration)

啟動設定是在 VS Code 中設定偵錯的傳統方式,它為執行複雜應用程式提供了最多的設定選項。

在本節中,我們將詳細介紹進階偵錯情境的設定和功能。您將找到關於使用來源對應 (source maps)跳過外部程式碼、進行遠端偵錯等的說明。

如果您想觀看入門影片,請參考 Getting started with debugging in VS Code

注意:如果您剛開始接觸 VS Code,可以在偵錯 (Debugging) 主題中了解一般偵錯功能及建立 launch.json 設定檔。

啟動設定屬性

偵錯設定儲存在工作區 .vscode 資料夾中的 launch.json 檔案內。關於建立和使用偵錯設定檔的介紹,請參考一般文章偵錯 (Debugging)

以下是 Node.js 偵錯器專用的常見 launch.json 屬性參考。您可以在 vscode-js-debug 選項文件中查看完整的選項集。

以下屬性在 launchattach 類型的啟動設定中皆受支援:

這些屬性僅適用於 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 configuration snippets for Node.js

您也可以透過 launch.json 編輯器視窗右下角的新增設定 (Add Configuration...) 按鈕來叫出程式碼片段。

Add Configuration button

以下提供可用的程式碼片段:

  • 啟動程式 (Launch Program):以偵錯模式啟動 Node.js 程式。
  • 透過 npm 啟動 (Launch via npm):透過 npm 'debug' 指令碼啟動 Node.js 程式。如果您的 package.json 中已定義 npm 偵錯指令碼,您可以在啟動設定中使用它。npm 指令碼中使用的偵錯連接埠必須對應到程式碼片段中指定的連接埠。
  • 附加 (Attach):附加至本機執行中的 Node.js 程式的偵錯連接埠。請確保要偵錯的 Node.js 程式已以偵錯模式啟動,且使用的偵錯連接埠與程式碼片段中指定的相同。
  • 附加至遠端程式 (Attach to Remote Program):附加至由 address 屬性指定的主機上執行的 Node.js 程式的偵錯連接埠。請確保要偵錯的 Node.js 程式已以偵錯模式啟動,且使用的偵錯連接埠與程式碼片段中指定的相同。為了協助 VS Code 在您的工作區和遠端主機的檔案系統之間映射來源檔案,請務必為 localRootremoteRoot 屬性指定正確的路徑。
  • 依處理序 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 屬性分別設為 externalTerminalintegratedTerminal,來啟用外部終端機或使用 VS Code 整合終端機。預設值為 internalConsole

在外部終端機中,您可以透過 terminal.external.windowsExecterminal.external.osxExecterminal.external.linuxExec 設定,選擇要使用的終端機程式。

對 'npm' 及其他工具的啟動設定支援

除了直接使用 node 啟動 Node.js 程式外,您也可以直接從啟動設定使用 'npm' 指令碼或其他任務執行器工具。

  • 您可以針對 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

將偵錯器附加到程式的選項

  • 開啟列出所有潛在處理序的「處理序選擇器」,讓您選擇其中一個,或者
  • 建立一個明確指定所有設定選項的「附加 (attach)」設定,然後按下 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 的輔助處理序成為可能。

設定「附加 (Attach)」設定

此選項需要更多步驟,但與前兩個選項不同的是,它允許您明確設定各種偵錯設定選項。

最簡單的「附加」設定如下所示:

{
  "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 偵錯器與偵錯目標斷開,後者會繼續執行。

如果偵錯工作階段處於「啟動 (launch)」模式,按下 Stop 會執行以下操作:

  1. 當第一次按下 Stop 時,會透過發送 SIGINT 訊號要求偵錯目標順利關閉。偵錯目標可以攔截此訊號,執行必要的清理工作,然後關閉。如果該關閉程式碼中沒有中斷點(或問題),偵錯目標和偵錯工作階段將會終止。

  2. 然而,如果偵錯器在關閉程式碼中觸及中斷點,或者偵錯目標無法自行正確終止,偵錯工作階段將不會結束。在此情況下,再次按下 Stop 將會強制終止偵錯目標及其子處理序 (SIGKILL)。

如果您發現按下紅色 Stop 按鈕後偵錯工作階段沒有結束,請再次按下該按鈕以強制關閉偵錯目標。

在 Windows 上,按下 Stop 會強制殺死偵錯目標及其子處理序。

來源對應 (Source maps)

VS Code 的 JavaScript 偵錯器支援來源對應,有助於偵錯轉譯語言,例如 TypeScript 或經壓縮/混淆的 JavaScript。有了來源對應,可以在原始碼中進行單步執行或設定中斷點。如果原始來源不存在來源對應,或者來源對應損毀且無法成功在原始碼與產生的 JavaScript 之間進行對應,則中斷點會顯示為未驗證(灰色空心圓圈)。

預設為 truesourceMaps 屬性用於控制來源對應功能。偵錯器總是嘗試使用來源對應(如果能找到的話),因此,您甚至可以使用 program 屬性指定原始碼檔案(例如 app.ts)。如果您因故需要停用來源對應,可以將 sourceMaps 屬性設為 false

工具設定

由於來源對應不一定會自動建立,您應確保設定您的轉譯器來建立它們。例如:

TypeScript

對於 TypeScript,您可以透過將 --sourceMap 傳遞給 tsc,或在 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",
  ]

智慧步進 (Smart stepping)

在啟動設定中將 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 中的每個路徑,而 sources 是一個路徑陣列。結果就是一組指向 .ts 檔案的絕對或相對路徑陣列。相對路徑會根據來源對應進行解析。

最後,偵錯轉接器會在此產生的 .ts 檔案清單中搜尋 app.ts 的完整路徑。如果找到符合項,它就找到了在將 app.ts 對應到 app.js 時要使用的來源對應檔案。如果沒有符合項,它就無法綁定中斷點,中斷點就會變成灰色。

當您的中斷點變成灰色時,以下是一些嘗試建議:

  • 在偵錯時,執行 Debug: Diagnose Breakpoint Problems 命令。此命令會從命令選擇區 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 開啟一個可以提供協助您解決問題的工具。
  • 您是否在啟用來源對應的情況下進行建置?請確保有 .js.map 檔案,或您的 .js 檔案中有內嵌的來源對應。
  • 您來源對應中的 sourceRootsources 屬性是否正確?它們結合起來能否得到指向 .ts 檔案的正確路徑?
  • 您是否以不正確的大小寫開啟了 VS Code 中的資料夾?有可能從命令列以 code FOO 這樣的指令開啟 foo/ 資料夾,這種情況下來源對應可能無法正確解析。
  • 嘗試在 Stack Overflow 上搜尋您特定設定的協助,或在 GitHub 上提交問題。
  • 嘗試加入 debugger 陳述式。如果它在那裡中斷進入 .ts 檔案,但該位置的中斷點無法綁定,那麼這對於在 GitHub 問題中提供相關資訊將非常有用。

覆寫來源對應路徑

偵錯器使用 sourceMapPathOverrides 來實作自訂的 sourcemap-to-disk 路徑對應。大多數工具都有良好的預設設定,但在進階情況下您可能需要進行自訂。預設的路徑覆寫是一個如下所示的物件對應:

{
  '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 capabilities)。使用 Remote Development 擴充功能,在遠端情境和容器中進行 Node.js 開發與本機設定並無不同。這是遠端偵錯 Node.js 程式的建議方式。請查看入門 (Getting started) 一節和遠端教學 (Remote tutorials) 以了解更多資訊。

如果您無法使用任何 Remote Development 擴充功能來偵錯您的 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 檔案瀏覽功能輕鬆找到並開啟它,您可以透過「執行與偵錯 (Run and Debug)」檢視中的 已載入指令碼 (LOADED SCRIPTS) 檢視來存取這些指令碼。

Loaded Scripts Explorer

已載入指令碼 (LOADED SCRIPTS) 檢視讓您可以透過鍵入指令碼名稱快速選擇它,或者在啟用 輸入時啟用篩選 (Enable Filter on Type) 時對清單進行篩選。

指令碼會載入到唯讀編輯器中,您可以在其中設定中斷點。這些中斷點會在偵錯工作階段間被記住,但您只能在偵錯工作階段執行時存取指令碼內容。

編輯原始碼時自動重新啟動偵錯工作階段

啟動設定的 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"
}

提示:按下 Stop 按鈕會停止偵錯工作階段並與 Node.js 斷開連線,但 nodemon (及 Node.js) 會繼續執行。若要停止 nodemon,您必須從命令列將其關閉(如果您使用上述的 integratedTerminal,這非常容易做到)。

提示:在發生語法錯誤的情況下,nodemon 將無法成功啟動 Node.js,直到錯誤修復為止。在此情況下,VS Code 會持續嘗試附加至 Node.js,但最終會放棄(10 秒後)。為避免這種情況,您可以加入一個值更大的 timeout 屬性(以毫秒為單位)來增加逾時時間。

重新啟動框架 (Restart frame)

Node 偵錯器支援在堆疊框架 (stack frame) 處重新啟動執行。這在您在原始碼中發現問題並想用修改後的輸入值重新執行一小部分程式碼時非常有用。停止然後重新啟動整個偵錯工作階段可能很耗時。重新啟動框架 (Restart Frame) 動作允許您在透過 設定值 (Set Value) 動作變更變數後重新進入目前的函式:

Restart frame

重新啟動框架不會回滾函式外部的狀態變更,因此它可能並不總是如預期般運作。

中斷點

條件式中斷點 (Conditional Breakpoints)

條件式中斷點是僅在表達式傳回真值 (truthy value) 時才暫停的中斷點。您可以透過在行號旁邊的裝訂線按右鍵並選擇「條件式中斷點 (Conditional Breakpoint)」來建立一個。

Conditional breakpoint

記錄點 (Logpoints)

有時您不想暫停,而是只想在程式碼執行到某個位置時記錄訊息或值。您可以使用 Logpoints 來達成。Logpoints 不會暫停,而是在觸及時將訊息記錄到偵錯控制台。在 JavaScript 偵錯器中,您可以使用大括號將表達式插入訊息中,例如 current value is: {myVariable.property}

您可以透過在行號旁邊的裝訂線按右鍵並選擇「Logpoint」來建立一個。例如,這可能會記錄像 location is /usr/local 這樣的內容:

Logpoint

命中次數中斷點 (Hit count breakpoints)

「命中次數條件 (hit count condition)」控制中斷點在「中斷」執行前需要被觸及多少次。您可以透過在行號旁邊的裝訂線按右鍵,選擇「條件式中斷點」,然後切換到「命中次數 (Hit Count)」來放置命中次數中斷點。

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) 區段中,這些中斷點會以箭頭顯示在要求行號與實際行號之間:

Breakpoints View

這種中斷點驗證發生在工作階段啟動並向 Node.js 註冊中斷點時,或者工作階段已在執行並設定新中斷點時。在此情況下,中斷點可能會「跳」到不同位置。在 Node.js 解析所有程式碼後(例如透過執行程式碼),可以使用 中斷點 (BREAKPOINTS) 區段標題中的 重新套用 (Reapply) 按鈕將中斷點輕鬆重新套用到所需位置。這應該會讓中斷點「跳回」到要求的位置。

Breakpoint Actions

跳過無關的程式碼

VS Code Node.js 偵錯具有一項功能,可避免逐步執行您不想偵錯的原始碼(也稱為「Just My Code」)。此功能可以透過啟動設定中的 skipFiles 屬性啟用。skipFiles 是一個用於路徑要跳過的 glob 模式陣列。

例如,使用:

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

專案中 node_moduleslib 資料夾中的所有程式碼都將被跳過。skipFiles 也適用於呼叫 console.log 和類似方法時顯示的位置:堆疊中第一個未跳過的位置將顯示在偵錯控制台的輸出旁邊。

Node.js 的內建核心模組可以在 glob 模式中以 '神奇名稱' <node_internals> 參照。以下範例跳過所有內部模組:

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

確切的 '跳過' 規則如下:

  • 如果您進入一個已跳過的檔案,您不會在那裡停止,而是在下一個已執行的非跳過檔案中的行停止。
  • 如果您設定了在拋出例外時中斷的選項,那麼除非例外冒泡到非跳過檔案,否則您不會在從跳過檔案拋出的例外處中斷。
  • 如果您在已跳過的檔案中設定了中斷點,您會在該中斷點處停止,並且能夠逐步執行,直到您跳出該檔案,此時將恢復正常的跳過行為。
  • 來自已跳過檔案內部的控制台訊息位置,將顯示為呼叫堆疊中第一個非跳過的位置。

已跳過的來源在「呼叫堆疊 (CALL STACK)」檢視中以「暗淡 (dimmed)」樣式顯示。

Skipped source is dimmed in call stack view

將滑鼠懸停在暗淡的條目上會說明為什麼該堆疊框架被調暗。

呼叫堆疊上的內容選單項目 切換跳過此檔案 (Toggle skipping this file) 讓您無需將檔案加入啟動設定,即可在執行階段輕鬆跳過該檔案。此選項僅在目前的偵錯工作階段期間持續有效。您也可以使用它來停止跳過在啟動設定中由 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,而應手動執行兩個指令進行建置:
  • 執行 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) 檢視中看到您的原始碼出現,且中斷點應該可以運作。

下圖中,偵錯器在建立 Mandelbrot 分形的 C++ 原始碼的中斷點處停止。呼叫堆疊可見,包含從 JavaScript 程式碼到 WebAssembly,再到對應的 C++ 程式碼的框架。您還可以查看 C++ 程式碼中的變數,以及對 int32 height 變數相關聯記憶體的編輯。

Debugger stopped on a breakpoint in C++ source code

雖然 WebAssembly 的偵錯功能已相當完善,但它與普通的 JavaScript 偵錯略有不同:

  • 「變數 (Variables)」檢視中的變數無法直接編輯。但是,您可以選擇變數旁邊的 檢視二進位資料 (View Binary Data) 動作來編輯其相關聯的記憶體。
  • 「偵錯控制台 (Debug Console)」和「監看 (Watch)」檢視中的基本表達式評估由 lldb-eval 提供。這與普通的 JavaScript 表達式不同。
  • 未對應到原始碼的位置將顯示為反組譯的 WebAssembly 文字格式。對於 WebAssembly,停用來源對應步進 (Disable Source Map Stepping) 命令會導致偵錯器僅在反組譯的程式碼中逐步執行。

VS Code 的 WebAssembly 偵錯功能是建立在 Chromium 作者的 C/C++ 偵錯擴充功能 (C/C++ Debugging Extension) 之上的。

支援的 Node 風格執行時期 (Runtime)

目前的 VS Code JavaScript 偵錯器支援 8.x 或以上的 Node 版本、較新的 Chrome 版本,以及較新的 Edge 版本(透過 msedge 啟動類型)。

後續步驟

如果您還沒閱讀 Node.js 一節,請查看:

  • Node.js - 包含範例應用程式的端對端 Node 情境

若要查看 VS Code 偵錯基礎知識的教學,請參閱此影片:

若要了解 VS Code 的任務執行支援,請前往:

  • 任務 (Tasks) - 使用 Gulp、Grunt 和 Jake 執行任務。顯示錯誤與警告

若要編寫自己的偵錯器擴充功能,請造訪:

常見問題

如何偵錯 ECMAScript 模組?

如果您使用 esm 或將 --experimental-modules 傳遞給 Node.js 以使用 ECMAScript 模組,您可以透過 launch.jsonruntimeArgs 屬性傳遞這些選項:

如何設定 NODE_OPTIONS?

偵錯器使用特殊的 NODE_OPTIONS 環境變數來設定應用程式的偵錯,覆寫它將導致偵錯功能無法正常運作。請不要覆寫它,而應附加到它後面。例如,.bashrc 檔案可能會有類似這樣的內容:

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

© . This site is unofficial and not affiliated with Microsoft.