头图
由于图片和格式解析问题,可前往 阅读原文

在现代前端与全栈开发的日常工作中,代码调试是不可或缺的一环,而一款优秀的调试工具能够显著提升开发效率,减少问题定位的时间成本。Visual Studio Code(简称 VSCode)作为一款备受开发者喜爱的轻量化编辑器,不仅提供了丰富的插件生态和高效的代码编辑体验,更以其强大的调试功能成为开发者工作流中不可或缺的一部分

无论是调试 Node.js 后端代码、前端浏览器代码,还是更复杂的容器化微服务系统,VSCode 都提供了灵活的调试支持。而这种强大的调试能力,并不仅限于简单地设置断点或查看变量,更体现在其对多种语言、运行时环境的无缝支持以及对调试配置的高度自定义能力上。通过掌握 VSCode 调试工具,开发者不仅能够轻松解决代码问题,还可以培养更系统化的调试思维,提升整体开发质量

在接下来的内容中,我们将围绕 VSCode 的调试功能展开深入剖析,从基本的断点设置到复杂的调试场景,带您全面解锁 VSCode 调试的潜能,助力高效开发

调试界面

和大多数调试器的功能模块界面基本上一致,包含:执行器、变量、执行栈、调试终端等等,比较简单这里就不多做介绍了;如果你对调试面板的操作步骤还不是很熟悉,可以看我的往期文章或者「vscode调试基本介绍」

调试分类

vscode支持多种形式的调试,从调试配置的复杂度来划分,可以分两大类:简单、复杂;

简单的调试可以快速的进入调试,无需关心过多的外在因素,通常这类的调试偏向于简单、无需相关工具辅助之类的项目,如:简单的原生js工程、NodeJS工程等等。简单的调试这里会介绍:auto attachJavascript Debug Terminal

而相对复杂的工程就不能使用这种模式了,当然也要具体的场景。面对复杂的场景,vscode提供了launch.json配置文件来定制化debug的功能,可以很方便的适配到不同的应用场景

Auto attach

auto attach顾名思义就是自动链接的意思,也就是当程序启动debug模式时,vscode就会自动连接到目标debug,然后根据vscode提供的调试UI进行调试

若要使用此功能需要在vscode中开启配置,cmd+shift+p 输入 >Debug: Toggle Auto Attach 后回车,支持多种形式的选项,大体分为:

  • always:当程序启动debug时总是自动连接上
  • flag:当监听到 --inspect--inspect-brk 时才自动连接
  • disabled:禁止
注意:当选择相关配置后,可能需要重启IDE

下图中小编启动的是flag模式,在IDE底部栏会有对应的显示,然后在终端输入 node --inspect script 启动程序,此时IDE就会自动连接上目标debug

可能心细得同学就已经发现了,当使用调试模式运行程序后,调试器会启动一个ws服务,为什么会有这个东西❓也有同学会疑问为啥vscode就能自动链接上目标debugger呢❓并且可以调试程序呢❓

现在让我们打开chrome浏览器并输入 chrome://inspect/#devices 打开调试TAB,会发现我们的目标程序,当点击 inspect 后会打开一个 Dev Tool 并且也可以调试目标程序

通过Chrome链接基本上可以肯定的是,调试UI会通过ws进行调试数据的传输,而vscode之所以会自动链接一定做了相关事件监听或者触发。后面部分我们会揭晓调试UI和目标Debugger之间的数据传输原理

如果没看到目标程序,需要将启动node程序后的ws服务ip和端口配置到 Discover network targets 即可

Javascript Debug Terminal

如果说上面的auto attach调试方式已经足够简单,那么Javascript Debug Terminal可以说是更加傻瓜式调试,简直有手都会系列‼️让我们来看看怎么个简单法

只需要打开Javascript Debug Terminal终端,然后像往常一样启动程序一样:

  1. 打开对应的终端
  2. 启动程序
vscode对于调试的友好性已经考虑到非常周全了

到这里就将简单形式的调试已经介绍完毕了,确实很简单,很时候一些需要快速、简单的调试场景,当然,实际场景中往往程序都是以复杂的工程出现的,那么这种简单的调试模式可能就不适用了,接下来看看vscode是如何支持复杂性工程的调试的

Debug Configuration

在 Visual Studio Code(VSCode)中,Debug Configuration(调试配置)是调试功能的核心。它允许开发者根据项目需求自定义调试环境、参数和行为。调试配置通常存储在项目的 .vscode/launch.json 文件中,通过 JSON 格式定义调试任务

创建launch.json文件有多种方式,支持手动、可视化等等,你可以打开Debug扩展,然后根据提示创建对应的文件:

在选择debugger时选择我们熟悉的node、chrome都行,最终都是可以通过配置文件调整

一个典型的 launch.json 文件由以下部分组成:

{
  "version": "0.2.0", // 配置文件的版本号,固定为 0.2.0
  "configurations": [
    // 调试任务列表
    {
      "type": "node", // 调试类型,例如 "node", "python", "chrome"
      "request": "launch", // 调试请求类型:"launch"(启动程序)或 "attach"(附加到运行的进程)
      "name": "测试NodeJS", // 调试任务的名称,显示在调试面板中
      "program": "${workspaceFolder}/index.js", // 主程序文件路径
      "cwd": "${workspaceFolder}", // 当前工作目录
      "env": {
        // 环境变量
        "NODE_ENV": "development"
      },
      "args": ["--verbose"], // 启动参数
      "port": 9229 // 调试所需的端口(仅部分配置需要)
    }
  ]
}

配置文件模式最大的区别就是type,总体来说它把调试划分成两大分类:

  • launch:以调试模式启动程序
  • attach:附加到正在运行的Debuger进程

创建配置

launch.json中可以快速创建对应类型的配置,vscode给了对应的配置

输入花括号后会有对应的提示

也点击右下角的Add COnfiguration按钮

Task

在正式介绍调试配置文件前,先来介绍下Task;这又是什么❓在 VSCode 中,Task 是一个功能强大的任务管理工具,用于执行各种自动化操作,例如运行构建工具、执行脚本、启动开发服务器、或调用外部命令。通过配置 Task,可以将常用的命令集成到 VSCode 中,大幅提升开发效率

说明了就是可以帮助我们执行一些自动化的任务,如:脚本、npm等等,结合调试就可以做一些自动化的东西。比如,我们需要调试一个目标程序前,可能需要将其编译才行,那么就可以将编译的任务交给Task,调试配置文件也确实提供了对应的属性:postLaunchTaskpreLaunchTask

{
  // postLaunchTask ...
  "preLaunchTask": "tsc: build" // 定义的 task 的 label 值
}

那么如何配置task呢,可以通过输入对应命令生成:.vscode/tasks.json

可以根据具体的场景调试配置:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "typescript",
            "tsconfig": "ts-examples/tsconfig.json",
            "problemMatcher": [
                "$tsc"
            ],
            "group": "build",
            "label": "tsc: build"
        }
    ]
}

接下来我们来看两大调试方式吧

attach

attach调试比较简单,就是链接到对应的目标Debugger调试程序进程上,以下是一个attach例子,通过attach方式链接到9229端口目标调试器上

{
  "name": "Attach NodeJS Break Debugger",
  "port": 9229,
  "address": "localhost",
  "request": "attach",
  "skipFiles": [
    "<node_internals>/**"
  ],
  "type": "node"
}

当我们在终端以调试的方式启动程序后,在调试插件界面,启动这个调试后就会自动链接上目标调试器:

这个其实和前面介绍的auto attach 以及用 Chrome 进行调试道理是一样的

此模式下也包含很多相关的属性配置,这里简单列举几个:

  • outFiles:源文件输出的目标文件,一般对于编译文件,vscode会自动匹配来做sourcemap映射
  • resolveSourceMapLocations:用于指定调试器在解析源映射(Source Maps)时可以加载的文件路径或 URL
  • continueOnAttach:用于控制调试器在附加到正在运行的进程时,是否自动继续执行程序,如:--inspect-brk模式调试时的场景

launch

如果说到这里还觉得配置文件方式的调试也比较的话,那么launch模式调试就会多样化了,我觉得它可以代表配置文件调试的核心,此种模式功能有很多

launch顾名思义就是启动的意思,的确它通过配置文件告诉vscode以debug形式启动目标程序,然后自动链接到调试UI上,这种模式自动化完成了启动与连接的过程

launch支持多种启动程序的方式,下面是常用形式:

  • url:通常是启动浏览器打开对应的地址
  • program:指定启动的应用程序文件
  • runtimeExecutable:通过执行器执行,如:npm、yarn、pnpm等等,这些执行器必须能正常使用

url

此种方式会直接打开设置的url,vscode内部会根据内置的策略将页面通过sourcemap与源码关联起来,如果程序需要编译但没有提供对应的sourcemap,将无法进行调试,此时打的断点都是灰色的

{
  "name": "Launch Chrome",
  "request": "launch",
  "type": "chrome",
  "url": "http://localhost:8082",
  "webRoot": "${workspaceFolder}"
}

请注意:以上url服务需要提前运,这里使用 http-server 指定 8082 端口运行

program

program则指定vscode要运行的程序文件,比如运行node程序main.js

{
  "type": "node",
  "request": "launch",
  "name": "Test Launch Debugger",
  "args": ["author=ihengshuai"],
  "program": "${workspaceFolder}/node/index.js",
  "env": { "website": "https://blog.usword.cn" }
}

此种形式通常可以搭配argsenv提供相关的参数变量等等

同样的如果程序也需要转义,请提供相关的sourcemap

runtimeExecutable

runtimeExecutable 用于指定调试程序的运行时可执行文件,该字段通常用于指定运行调试目标所需的特定运行环境,例如 Node.js 的可执行文件、Python 解释器或其他自定义的可执行程序

{
  "type": "node",
  "request": "launch",
  "name": "Test Launch Debugger",
  "skipFiles": [
    "<node_internals>/**"
  ],
  "cwd": "${workspaceFolder}/node",
  "args": ["author=ihengshuai"],
  "runtimeExecutable": "npm", // 使用npm启动
  "runtimeArgs": ["run", "debug", "github=ihengshuai"], // npm 后面的参数, npm run debug
  "env": { "website": "https://blog.usword.cn" }
}

此种方式的效果图和以上基本一致

重要的属性

launch方式调试有多种属性可以选择,这里我们列举几个

  1. args:用来给目标程序传递对应的变量
  2. cwd:指定启动目标程序的基本路径,一般都会在项目的根目录,如果对于多仓库项目,可以你可以指定对应的文件路径
  3. runtimeArgs:传给runtimeExecutable 的参数,比如对应的执行脚本命令:build、lint等等
  4. env和envFile:环境变量,和.env配置文件定义类似
  5. console:启动程序的终端,默认是debug终端,如果对于程序执行过程中一些特殊要求,你可以选择vscode终端或者外部的终端,有internalConsoleintegratedTerminalexternalTerminal几个配置
除了这些属性配置外,可以很多其他的属性选项,请结合不同的type提示选择使用对应的属性

设置sourcemap

对于大多数的真实场景来说,都会是基于各种打包器的比较复杂的大型工程,那么生成的文件都会是经过压缩或者含义后的内容,而调试则需要对应sourcemap的支持,这样才能和源码映射起来

配置文件提供了sourcemap相关的配置:

  • sourceMaps:默认为true,启用sourcemap
  • outFiles:用于指定编译后的文件路径的位置,调试器会使用这些文件来加载 Source Map
  • webRoot:用于指定 Web 应用的根目录
  • sourceMapPathOverrides:用于覆盖和重映射源代码路径,当 Source Map 文件中记录的路径与实际文件路径不一致时,通过此字段解决路径匹配问题
  • resolveSourceMapLocations:控制调试器加载 Source Map 文件的路径范围

比如我们基于webpack进行路径映射的调整:

{
  "sourceMapPathOverrides": {
    "webpack:///./*": "${webRoot}/src"
  },
}
注意:以上只是简单的一个例子,请根据具体的项目场景设置

分组调试

vscode支持将多个调试分组,可以按组运行调试器

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Chrome Browser",
      "request": "launch",
      // ...
    },
    {
      "name": "Launch Node Server",
      "type": "node",
      // ...
    },
  ],
  "compounds": [ // 定义组
    {
      "name": "Run Debugger",
      "configurations": ["Launch Chrome Browser", "Launch Node Server"]
    }
  ]
}

到这里基本上将vscode的调试功能讲完了,接下来我们来几个真实的调试案例

调试案例

调试Vue源码

配置好调试,终端启动对应的服务脚本:

启动debug会打开对应的浏览器,这里我们点进对应的文件目录和文件:

在对应的源码文件中打断点:

调试NodeJS

{
  "name": "Test Express App",
  "request": "launch",
  "runtimeArgs": [
    "run-script",
    "express-app"
  ],
  "runtimeExecutable": "npm",
  "skipFiles": [
    "<node_internals>/**"
  ],
  "type": "node"
}

调试NestJS

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Test NestJS",
      "request": "launch",
      "runtimeArgs": [
        "run-script",
        "start:dev"
      ],
      "runtimeExecutable": "pnpm",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "type": "node"
    }
  ]
}

调试Typescript

由于目标程序是ts,所以需要sourcemap的支持,上面我们讲了几种方式配置

{
  "name": "Launch Typescript",
  "type": "node",
  "request": "launch",
  "skipFiles": [
    "<node_internals>/**"
  ],
  "program": "${workspaceFolder}/ts-examples/demo1.ts",
  "outFiles": [
    "${workspaceFolder}/ts-examples/dist/*.(m|c|)js",
    "!**/node_modules/**"
  ],
}

调试SSR

ssr有服务端和前端,我们可以使用组合方式进行调试,页面通过启动浏览器的方式进行调试,node端通过脚本形式启动服务,这样就可以调试了

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Chrome Browser",
      "request": "launch",
      "type": "chrome",
      "url": "http://localhost:10011",
      "webRoot": "${workspaceFolder}/client",
      "sourceMaps": true,
    },
    {
      "name": "Launch Node Server",
      "type": "node",
      "request": "launch",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["dev"],
      "sourceMapPathOverrides": {
        "webpack:///./*": "${webRoot}/src"
      },
    },
  ],
  "compounds": [
    {
      "name": "Run Debugger",
      "configurations": ["Launch Chrome Browser", "Launch Node Server"]
    }
  ]
}
切记soucemap支持

调试其他语言

vscode不光支持js语言的调试,同时它也支持其他语言的调试,如:Java、C#、Python等等,如果你需要调试其他语言,可以下载对应的语言的调试插件 Debug Extensions Marketplace

核心原理

从上面的attach模式我们就知道目标程序启动debug模式后,可以和不同的调试UI进行连接通信,然后完成一系列的调试,而调试的核心就是debugger和UI进行数据通信的形式,只要两端根据协商好的协议进行数据的传输,就可以完成一系列操作

Chrome DevTools Protocol(CDP)

Chrome DevTools Protocol (CDP) 是由 Google 提供的一套调试协议,用于调试、诊断、分析和操作运行中的 Chrome 浏览器或其他基于 Chromium 的环境。它允许开发者通过远程调用控制浏览器的行为,比如拦截网络请求、调试 JavaScript、操作 DOM、性能分析等

开启Protocol Monitor

查看Protocol Monitor

Debug Adapter Protocol (DAP)

VSCode 使用 Debug Adapter Protocol (DAP) 作为调试的标准协议。由于vscode支持多种语言的调试,因此中间使用适配协议,不同语言的调试器是要对接了对应的适配器数据定义,就可以完成数据通信

你也可以直接阅读官方的「Debugging Architecture of VSCode」

参考文档

由于图片和格式解析问题,可前往 阅读原文

大卫talk
71 声望9 粉丝

攻粽号:码上来財(mslaicai)