如何在 VSCode 中设置断点,直接调试 Vue 代码?
以下所有运行环境的 node 版本:v16.15.1
1 vue-cli 4 创建的 vue 项目 以下操作和设置,在由 vue-cli 4 创建的 vue2/js 项目下进行,应该也适用于 vue-cli 4 下创建的其它类型项目,没有进一步尝试。
vue-cli 4 对应的 webpack 版本为 4.x。
一些前置条件 VSCode 已经内置 JaveScript Debugger,不需要再安装 Debugger for Chome。
microsoft/vscode-js-debug: The VS Code JavaScript debugger
VSCode 需要配置 launch.json
Debugging in Visual Studio Code
在项目中,添加 launch.json 文件,可以选择多种调试器。
如果已经创建,则还可以添加配置。
这里是配置属性的含义说明:
Debugging in Visual Studio Code
launch.json 配置 需要添加 sourceMapPathOverrides
vscode-js-debug/OPTIONS.md at ab0b7d74c66a837c2ab6918f7bb81a8fc3c61663 · microsoft/vscode-js-debug
{ "version" : "0.2.0" , "configurations" : [ { "name" : "Launch Chrome" , "request" : "launch" , "type" : "chrome" , "url" : "http://localhost:8080" , "webRoot" : "${workspaceFolder}/src" , "sourceMapPathOverrides" : { "webpack:///src/*" : "${webRoot}/*" } } ] }
注意 webRoot 要设置正确,需要和 sourceMapPathOverrides 中的路径对应。
vue.config.js 配置 webpack 相关 | Vue CLI
添加 vue.config.js 配置文件,添加 webpack 配置。
module .exports = { configureWebpack : { devtool : 'source-map' } }
调试运行 在终端中使用 yarn serve 运行,然后在 vscode 启动调试,就可以命中断点了。
2 vue-cli 5 创建的 vue 项目 vue-cli 5 对应的 webpack 版本为 5.x,在 vue-cli 5 下创建的 vue 项目,不能通过类似上面比较简单的设置,就开启在 VSCode 中的断点调试支持。
以下的配置在 vue2/js,vue3/js,vue3/ts 下测试 OK,但操作有点复杂,不像是支持一个调试功能需要的设置,不然门槛有点太高了。如果知道其它简单的方式,求告知。
方案来源:Vue + TypeScript & Debuggers - ckh|Consulting
准备工作
添加 source-map 包,被安装的版本为 “^0.7.4”。
launch.json 配置
{ "version" : "0.2.0" , "configurations" : [ { "name" : "Launch Chrome" , "request" : "launch" , "type" : "chrome" , "url" : "http://localhost:8080" , "webRoot" : "${workspaceFolder}" , "sourceMapPathOverrides" : { "webpack://<name-from-package.json>/./src/*" : "${webRoot}/src/*" }, } ] }
注意,上面的 <name-from-package.json> 需要替换为项目名称
vue.config.js
const { defineConfig } = require ('@vue/cli-service' )const fs = require ('fs' )const { SourceMapConsumer , SourceMapGenerator } = require ('source-map' )module .exports = defineConfig ({ transpileDependencies : true , configureWebpack : { devtool : 'source-map' , plugins : [ { apply (compiler ) { compiler.hooks .thisCompilation .tap ('Initializing Compilation' , compilation => { compilation.hooks .finishModules .tapPromise ('All Modules Built' , async modules => { for (const module of modules) { if (shouldSkipModule (module )) continue const pathWithoutQuery = module .resource .replace (/\?.*$/ , '' ) const sourceFile = fs.readFileSync (pathWithoutQuery).toString ('utf-8' ) const sourceMap = extractSourceMap (module ) sourceMap.sources = [pathWithoutQuery] sourceMap.sourcesContent = [sourceFile] sourceMap.mappings = await shiftMappings (sourceMap, sourceFile, pathWithoutQuery) } }) }) } } ] } }) function shouldSkipModule (module ) { const { resource = '' } = module if (!resource) return true if (/node_modules/ .test (resource)) return true if (!/\.vue/ .test (resource)) return true if (!/type=script/ .test (resource)) return true if (!/lang=ts/ .test (resource)) return true if (isMissingSourceMap (module )) return true return false } function isMissingSourceMap (module ) { return !extractSourceMap (module ) } function extractSourceMap (module ) { if (!module ['_source' ]) return null return module ['_source' ]['_sourceMap' ] || module ['_source' ]['_sourceMapAsObject' ] || null } async function shiftMappings (sourceMap, sourceFile, sourcePath ) { const indexOfScriptTag = getIndexOfScriptTag (sourceFile) const shiftedSourceMap = await SourceMapConsumer .with (sourceMap, null , async consumer => { const generator = new SourceMapGenerator () consumer.eachMapping (mapping => { const { generatedColumn, generatedLine, originalColumn, originalLine } = mapping let name = mapping.name let source = sourcePath var original = null if (originalLine === null || originalColumn === null ) { name = null source = null } else { original = { column : originalColumn, line : originalLine + indexOfScriptTag } } generator.addMapping ({ generated : { column : generatedColumn, line : generatedLine }, original, source, name }) }) return generator.toJSON () }) return shiftedSourceMap.mappings } function getIndexOfScriptTag (sourceFile ) { const lines = sourceFile.match (/.+/g ) let indexOfScriptTag = 0 for (const line of lines) { ++indexOfScriptTag if (/<script/ .test (line)) break } return indexOfScriptTag }
以上代码还是来自 Vue + TypeScript & Debuggers - ckh|Consulting ,源码见:vue-typescript-debugger/vue.config.js at master · fearnycompknowhow/vue-typescript-debugger
需要注意的是,以上源码,做了一点小修复:添加了 var original = null 的定义,不然编译可能报错。
运行调试 通过 yarn serve 运行项目,在 VSCode 中启动调试,就可以命中断点了。
注意,launch.json 的配置的启动 url,需要随配置修改。
3 vite 创建的 vue 项目
这里的 vite 版本是 2.9.13
npm info vite version
如果使用 vite 创建 vue 项目,不管是 vue2、vue3,还是 js/ts,让 VSCode 支持调试,都非常简单。
开始 | Vite 官方中文文档
yarn create vite
launch.json
只要添加 “webRoot”: “${workspaceFolder}/src” 就可以了
{ "version" : "0.2.0" , "configurations" : [ { "name" : "Launch Chrome" , "request" : "launch" , "type" : "chrome" , "url" : "http://localhost:3000" , "webRoot" : "${workspaceFolder}/src" } ] }
运行调试 然后直接运行,yarn dev,接着就可以在 VSCode 中启动调试了。
4. 参考文章 在 VS Code 中调试 Vue.js - SegmentFault 思否
Vue + TypeScript & Debuggers - ckh|Consulting