![1e77cc8a3510420adcc36b257b7df1fd.png](https://img-blog.csdnimg.cn/img_convert/1e77cc8a3510420adcc36b257b7df1fd.png)
(首发于知乎,如需转载请注明出处)
笔者以往web前端项目通常使用Chrome DevTools来进行调试(debug),编码则用的是VSCode,一直以来这款ide以它自带的调试功能作为一大卖点,于是今天来对VSCode调试功能的用法一探究竟,用它调试手头上的一个webpack-dev-server项目。
0.准备就绪
按照在网上查阅的资料[1],了解到想要对Chrome打开的JavaScript页面进行调试,需要在VSCode安装这样一个扩展:
![cb17811890c55949ab2b02b498073d2a.png](https://img-blog.csdnimg.cn/img_convert/cb17811890c55949ab2b02b498073d2a.png)
下载安装很顺利,接着就开始做debug的配置。打开Run and Debug
面板,在RUN AND DEBUG下拉框选择Add Config
,VSCode自动创建launch.json
文件,在光标处键入launch,选择Chrome: Launch
,一套用于Chrome启动调试模式的配置模板就出来了:
![b9766e39d32c32a553cd4003f4f4d146.png](https://img-blog.csdnimg.cn/img_convert/b9766e39d32c32a553cd4003f4f4d146.png)
由于笔者的项目是通过npm加载webpack-dev-server搭建的开发环境,故还需要在真正开始debug前运行npm start
命令启动server,怎么办呢?
所幸VSCode周到如斯,为我们提供了一个preLaunchTask
属性——我们可以指定在debug前要运行的命令:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}",
"preLaunchTask": "debug"
}
]
}
同步在项目的tasks.json
文件(通过命令面板输入>Task
,选择Tasks: Configure Task
)中加入运行命令的Task配置:
{
"version": "2.0.0",
"command": "npm", // Task 要运行的命令
"tasks": [
{
"label": "debug", // Task 名称,在命令面板中显示
"isBackground": true,
"type": "npm",
"script": "start", // npm 要执行的 script 名称,对应 package.json 中的定义
"path": "fe/", // 执行命令所在的目录,相当于 cd 命令
"detail": "编译至开发环境", // Task 的描述,在命令面板中显示
"group": "test",
"problemMatcher": {
"owner": "typescript",
"fileLocation": "relative",
"pattern": {
"regexp": "^([^s].*)((d+|,d+|d+,d+,d+,d+)):s+(error|warning|info)s+(TSd+)s*:s*(.*)$",
"file": 1,
"location": 2,
"severity": 3,
"code": 4,
"message": 5
},
"background": {
"activeOnStart": true,
"beginsPattern": ".",
"endsPattern": "Version: webpack.+"
}
}
}
]
}
注意上面这一大段并非都是必要的,反复测试后,总结出只有下面这些属性必须指定,其他将自动取默认值:
version
、command
——配置的版本号、要运行的命令;label
——Task的名称;isBackground
——指示Task是否在后台运行,必须为true
,告诉VSCode可以在Task运行时启动调试会话,具体的启动时机由后面的background.endsPattern
指定;problemMatcher.pattern.regexp
——编辑器从命令输出提取警告/错误/提示消息使用的pattern;background
——指示Task后台运行时,VSCode如何跟踪Task运行状态,具体是依靠Task所执行命令的输出来判断的,两个pattern属性分别表示Task启动或结束时输出的消息特征(注意特征不惟一,可以从TERMINAL面板的输出自行确定),VSCode在接收到符合pattern的消息后开始或停止对Task的跟踪。当跟踪停止后,VSCode按launch.json
配置启动调试会话(如不指定pattern,VSCode不会对tasks.json
报错,但运行Task 10秒后将提示The specified task cannot be tracked.
并中止调试)。
1.见证奇迹的时刻
做好以上两块配置后,就可以按下F5
键启动调试了:
![477d3608956aa72293ba88be4d2ef813.png](https://img-blog.csdnimg.cn/img_convert/477d3608956aa72293ba88be4d2ef813.png)
然鹅,我们很快就会发现,虽然VSCode可以像浏览器一样正常输出消息,但在对源码中加入断点时,有些行的断点明明可以加,编辑器却加不上去。什么情况?
2.配置Source Map
这还要从webpack编译讲起。由于JS代码经过编译、打包才最终成为浏览器中实际运行的代码,其内容已经与原来的代码大相径庭。比方说同样是88行,在源码这是一句方法调用,到了实际代码那里就只是一个大括号。这就解释了为什么有些行的断点加不上去的现象。
![2e5e78f5fc172fbe81d0c0633a59681c.png](https://img-blog.csdnimg.cn/img_convert/2e5e78f5fc172fbe81d0c0633a59681c.png)
为了确保断点信息能被VSCode正确读取,浏览器需要知道原始代码和打包代码的映射关系,这就是SourceMap要干的事情。让我们在webpack.config.js
加入导出SourceMap的选项:
module.exports = {
// 若构建速度较慢,可改用 cheap-module-source-map 或 cheap-module-eval-source-map 模式,去掉列映射
// 不建议使用 source-map,除非你不赶时间,也不在乎源码安全问题
devtool: 'eval-source-map',
...
};
![17d18dfe4735b161dd37c2da30943e26.png](https://img-blog.csdnimg.cn/img_convert/17d18dfe4735b161dd37c2da30943e26.png)
重新启动调试,已经可以正常显示所打的断点了。下面总算可以进行正常的调试了吧?
然鹅,当笔者在Index.jsx:749行加入断点后,加是加上去了,却发现运行时,这个断点并没有被触发。难道VSCode出bug了?还是说少了什么配置?
![6ab89a86e08c7f7b364a23d9accafaa3.png](https://img-blog.csdnimg.cn/img_convert/6ab89a86e08c7f7b364a23d9accafaa3.png)
3.sourceMapPathOverrides
属性
于是笔者又继续查阅网上资料,注意到了一篇关于VSCode debugger的应用介绍[2]里面的代码:
...
"webRoot": "${workspaceFolder}",
"sourceMapPathOverrides": {
"webpack:///./*": "${webRoot}/*",
"webpack:///src/*": "${webRoot}/*",
"webpack:///*": "*",
"webpack:///./~/*": "${webRoot}/node_modules/*",
"meteor:// app/*": "${webRoot}/*"
},
...
放到launch.json
里面,VSCode对sourceMapPathOverrides
属性的说明是:
A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.
而且,看到这一串webpack:///,笔者联想到Chrome DevTools里面对模块文件提示的URL:
![a71ebacaa057c8fc678757ca5c9ead51.png](https://img-blog.csdnimg.cn/img_convert/a71ebacaa057c8fc678757ca5c9ead51.png)
注意到这两点,笔者突然就明白了。
原来,sourceMapPathOverrides
这个属性,是VSCode用来定位调试源文件的配置信息。设置了这个属性,VSCode才会在工作区目录下的特定位置寻找源文件,并将其关联到调试中。
而对应到笔者这个项目,源文件所在目录并非与VSCode工作区目录一致,而是工作区目录下的一个子目录fe/src
,所以launch.json
还要加上对SourceMap映射的源文件的定位配置,如下:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}/fe", // 相对于工作区目录下的 fe 子目录寻找源文件
"sourceMapPathOverrides": {
"webpack:///./*": "${webRoot}/*",
"webpack:///src/*": "${webRoot}/*",
"webpack:///*": "*",
"webpack:///./~/*": "${webRoot}/node_modules/*",
},
"preLaunchTask": "debug"
}
]
}
按Ctrl+Shift+F5
键重新运行调试,bingo!
![ef067c2320dfbc96ff9d87c66edbd306.png](https://img-blog.csdnimg.cn/img_convert/ef067c2320dfbc96ff9d87c66edbd306.png)
4.总结
重温一下这次基于VSCode的调试配置过程,可以发现这款ide在定制调试方式上提供了相当丰富的配置,应该说给予了开发人员相当大的自由度,但不同类型的JS项目所需做的配置各有要点、各不相同,初学者还是需要花一定的时间琢磨才能上手并融会贯通。通过这次体验VSCode调试功能,笔者也体会到了程序调试这门基本功之博大精深,后面还会抽空写一下如何在VSCode中利用Attach方式对外部NodeJS进行调试。
参考
- ^使用 Visual Studio Code 调试 webpack + react 项目 https://juejin.im/post/5ab3560d6fb9a028dc40ec99
- ^Debug browser code in vscode https://vcfvct.wordpress.com/2019/01/11/debug-browser-code-in-vscode/