vscode调试 Vue 项目原创
Vue 项目的创建有两种方式:
- 用 @vue/cli 创建的 webpack 作为构建工具的项目
- 用 create-vue 创建的 vite 作为构建工具的项目
# 调试 @vue/cli 创建的 webpack 项目
首先安装 @vue/cli:
yarn global add @vue/cli
然后执行以下命令创建 vue 项目:
vue create vue-demo1
选择 vue3 的模版。
安装完之后进入到 vue-demo1 目录,执行 npm run serve
把开发服务跑起来。
浏览器访问,会看到渲染出的页面:
然后我们进行调试:
点击调试窗口的 create a launch.json file
来创建调试配置文件:
把 Chrome 调试配置的 url 改成目标 url 就可以进行调试了:
点击 debug 启动,在 vue 组件里打个断点,你会发现断点没生效:
这是为什么呢?
我们先加个 debugger 来跑一下:
然后在 Chrome DevTools 里看下:
你会发现他从一个乱七八糟的路径,映射到了 webpack://vue-demo1/src/App.vue?11c4
的路径下。
然后在 VSCode Debugger 里看看这个路径:
发现是 /Users/guang/code/vue-demo1/src/App.vue?11c4
本地明显没这个文件,所以就只读了。
其实这个路径已经做过了映射,就是完成了从 webpack:///vue-demo1/src/App.vue?11c4
到 /Users/guang/code/vue-demo1/src/App.vue?11c4
的映射。
看一下 sourceMapPathOverrides 默认这三条配置,很容易看出是最后一条做的映射:
"sourceMapPathOverrides": {
"meteor://app/*": "$workspaceFolder}/*",
"webpack:///./~/*": "$workspaceFolder}/node modules/*",
"webpack://?:*/*": "${workspaceFolder}/*"
}
2
3
4
5
但问题就出现在后面多了一个 ?hash 的字符串,导致路径不对了。
那为什么会多这样一个 hash 呢?
这是因为 vue cli 默认的 devtool 设置是 eval-cheap-module-source-map
,前面讲过,eval 是每个模块用 eval 包裹,并且通过 sourceURL 指定文件路径,通过 sourceMappingURL 指定 sourcemap。
在 Chrome DevTools 里点击下面的 source map from 的 url:
你会发现先映射到了一个中间文件:
这个是被 eval 包裹并指定了 sourceURL 的模块代码,会被 Chrome DevTools 当作文件加到 sources 里。
这里有两个 sourceURL,第一个 sourceURL 在 sourceMappingURL 之前,这样 sourcemap 映射到的就是这个 url,也就是被 Chrome DevTools 当作文件的路径。而第二个 sourceURL 在之后,它可以修改当前文件的 url,也就是在调试工具里展示的路径。
然后再点击,会跳转回 bundle 的代码:
这些被 eval 包裹的就是一个个的模块代码。
这些是上节讲过的内容,这样有啥问题么?
第一个 sourceURL 的路径是通过 [module] 指定的,而模块名后默认会带 ?hash:
所以想要去掉 hash 就不能用 eval 的方式。
所以我们修改下 webpack 的 devtool 配置:
从 eval-cheap-module-source-map
变为 ~source-map。~
去掉 eval 是为了避免生成 ?hash 的路径,去掉 cheap 是为了保留列的映射,去掉 module 是因为这里不需要合并 loader 做的转换。
然后重启跑一下 dev server,再次调试:
这时你会发现之前不生效的断点现在能生效了:
去 Chrome DevTools 里看一下,路径后也没有 ?hash 了:
这样就能愉快的调试 vue3 的代码了。
如果你创建的是 vue2 项目,可能还要在 launch.json 的调试配置加这样一段映射(只保留这一条):
"sourceMapPathOverrides": {
"webpack://你的项目名/src/*": "${workspaceFolder}/src/*"
}
2
3
这个项目名就是 project 的名字,你也可以在代码里打个断点,在 Chrome DevTools 里看:
我们映射的目的就是把这个路径映射到本地目录。
如果你在 chrome devtools 里看到的路径没有项目名:
那就直接这样映射:
"sourceMapPathOverrides": {
"webpack:///src/*": "${workspaceFolder}/src/*"
}
2
3
绝大多数情况下,这样样配就行了。
但有的项目可能 VSCode 还是没映射对,这时候你可以自己映射一下,打个断点看看在 Chrome DevTools 里是什么路径,然后看看本地是什么路径,配置对应的映射就好了。
知道了 vue cli 创建的 webpack 项目怎么调试,我们再来看下 create vue 创建的 vite 项目:
# 调试 create vue 创建的 vite 项目
create vue (opens new window) 是创建 vite 作为构建工具的 vue 项目的工具。
直接执行npm init vue@3
即可:
进入 vue-demo2 目录,执行安装,启动开发服务器:
浏览器访问,可以看到渲染出的页面:
我们添加一个调试配置如下:
{
"type": "chrome",
"request": "launch",
"name": "调试 vite 项目",
"runtimeExecutable": "canary",
"runtimeArgs": [
"--auto-open-devtools-for-tabs"
],
"userDataDir": false,
"url": "http://localhost:5174",
"webRoot": "${workspaceFolder}/aaa"
}
2
3
4
5
6
7
8
9
10
11
12
这里设置 userDataDir 为 false,是使用默认用户数据目录,不然 Vue DevTools 之类的插件就要再次安装了。
打个断点,然后 Debug 启动:
我们找个 vue 文件打个断点:
修改下 HelloWorld.vue 的代码,然后打两个断点:
重新启动调试:
两个断点都能生效,代码也能直接修改。
还有,调试 @vue/cli 创建的项目时,我们还映射了下 sourcemap 的 path,为啥 create vue 的项目就不需要了呢?
看下 sourcemap 到的文件路径就知道了:
运行的代码文件的路径是:
sourcemap 到的文件路径是:
从 http://localhost:5174
后开始,把/src/components/HelloWorld.vue
文件sourcemap到了 /Users/guang/code/vue-demo2/src/components/HelloWorld.vue
这已经能够对应到本地的文件了,自然也就不需要 sourceMapPathOverrides 的配置。
至此,create vue 创建的 vue 项目我们也知道怎么调试了。
有同学可能会问,为什么 webRoot 要配置成 ${workspaceFolder}/aaa 呢?
因为 Vite 项目有一些热更的文件,这些临时文件没有对应的本地文件,但路径刚好是 VSCode Debugger 传递过去的打断点的文件路径,就断住了,所以你就会发现断点断在了奇怪的文件:
为了避免这种情况,我们配置了 webRoot,那实际上传过去的断点信息就是 /aaa/src/App.vue。
这样热更的文件和这个路径就不一样了,也就不会断住。
而有 sourcemap 的文件,因为 sourcemap 到的是绝对路径,不受 webRoot 的影响,依然能映射到本地,所以那些断点能生效。
这就是为什么调试 vite 项目要配置下 webRoot,加上一个本地不存在的目录。
# 总结
这节我们调试了下 vue 项目。
vue 项目有两种创建方式,@vue/cli 和 create vue,分别是创建 webpack 和 vite 作为构建工具的项目。
vue cli 创建的项目,默认情况下打断点不生效,这是因为文件路径后带了 ?hash,这是默认的 eval-cheap-module-source-map 的 devtool 配置导致的,去掉 eval,改为 source-map 即可。
create vue 创建的 vite 做为构建工具的项目 sourcemap 到的路径直接就是本地的路径了,更简单一些。但是会有一些文件被错误映射到源码的问题,需要设置下 webRoot。
学会了 vue 项目的调试,接下来就可以愉快的边调试边写代码了。
# 附录
1、launch.json常用配置
{
"version": "0.2.0",
"configurations": [
// 使用yarn启动项目
{
"name": "Launch with yarn",
"request": "launch",
"runtimeArgs": [
"run",
"docs:dev"
],
"console": "integratedTerminal",
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///docs/*": "${workspaceFolder}/docs/*"
},
"runtimeExecutable": "yarn",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
},
//断点调试
{
// 配置类型
// "type": "chrome",
"type": "msedge",
// 请求类型
"request": "launch",
// 配置名称
"name": "vuejs: chrome",
// 访问的URL地址
"url": "http://localhost:8080",
// 是否自动打开开发者工具
// "runtimeArgs": [
// "--auto-open-devtools-for-tabs"
// ],
// "runtimeExecutable": "edge",
"userDataDir": false,
// 项目根目录
"webRoot": "${workspaceFolder}",
// 是否启用源码映射
"sourceMaps": true,
// 源码映射路径重定义
"sourceMapPathOverrides": {
// 用于替换webpack路径中的docs路径
"webpack:///docs/*": "${webRoot}/docs/*",
// 用于替换webpack路径中的node_modules路径
"webpack:///node_modules/*": "${webRoot}/node_modules/*",
// 这是webpack的协议,用于匹配所有路径,用于调试js
// 匹配规则是webpack://开头,?:*表示匹配但不映射,再后面是一个斜杠和星号,表示匹配剩余路径
// 右侧的值是路径替换规则,使用了变量${webRoot},表示要替换为webRoot变量的值
"webpack://?:*/*": "${webRoot}/*"
},
},
//调试当前js文件
{
"type": "node",
"request": "launch",
"name": "Launch Node",
"skipFiles": [
"<node_internals>/**"
],
"program": "${file}"
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
- 01
- element-plus多文件手动上传 原创11-03
- 02
- TrueLicense 创建及安装证书 原创10-25
- 03
- 手动修改迅捷配置 原创09-03
- 04
- 安装 acme.sh 原创08-29
- 05
- zabbix部署 原创08-20