从vs代码调试vuejs:在chrome中调试vuejs应用程序时的未绑定断点



版本:

Visual Studio Code: Version: 1.59.1 (Universal)
vueJS: 3.0.0
Chrome: Version 94.0.4606.61 (Official Build) (x86_64)

我使用的是VS Studio代码中内置的javascript调试器。我的应用程序结构(在IDE中)如下:

  • Root(Maven父项目)
    • 后端(Java中的Maven子项目)
    • frontend(vueJS中的Maven子项目)

也就是说,我有一个Java后端,它为vueJS前端提供服务,所有这些都捆绑在Tomcat web归档文件(即war文件)中。这实际上运行得很好,而且我可以在VS Studio code中使用Tomcat扩展调试Java代码。

问题是调试vueJS逻辑。请注意,我的vueJS应用程序包含了TypeScript插件。vs代码中的调试vuejs启动得很好(在正确的调试配置下,如下所示),我可以设置一个浏览器断点,它实际上会触发IDE中的中断。因此,VS Studio Code和Chrome之间的基本握手是合理的。因此,我怀疑配置——即launch.jsontsconfig.json或其他IDE设置——不太正确。详情如下。

vue.config.json:

module.exports = {
// Change build paths to make them Maven compatible; see https://cli.vuejs.org/config/.
outputDir: 'target/dist',
assetsDir: 'static',
publicPath: '/myapp',
configureWebpack: {
devtool: "source-map"
}
}

在这里,我已经为生产中的webpack缩小文件(即在Chrome中运行的客户端脚本)启用了源代码映射。请注意,我的应用程序的根目录是Chrome中的/myapp

launch.json:

{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"name": "vuejs: chrome",
"request": "launch",
"url": "http://localhost:8080/myapp",
"breakOnLoad": true,
"webRoot": "${workspaceFolder}/frontend",
"outFiles": ["${workspaceFolder}/frontend/target/dist/static/js/*.js"],
"vueComponentPaths": [
"${workspaceFolder}/frontend/src/**/*.vue"
],
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///myapp/static/js/*": "${webRoot}/src/*",
"webpack:///./~/*": "${webRoot}/node_modules/*",
"webpack:////*": "/*",
"webpack://?:*/*": "${webRoot}/src/*",
"webpack:///([a-z]):/(.+)": "$1:/$2",
"webpack:///src/*": "${webRoot}/src/*",
}
}
]
}

这里,${workspaceFolder}正好对应于我的源repo的根。sourceMapPathOverrides目前一团糟——这是默认映射和我自己将Chrome端javascript资源路径映射到IDE中引用的源repo路径的尝试(因此没有结果)的结合。

ts.config.json:

{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// "inlineSourceMap": true,
// "inlineSources": true,
"sourceMap": true,
"baseUrl": ".",
"resolveJsonModule": true,
"types": [
"webpack-env",
"jest"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

一位程序员建议去掉sourceMap布尔值,改为指定inlineSourceMapinlineSources。我已经评论掉了这些设置,因为我无法确定它们是否有帮助。

vueJS构建生成到target/dist的输出,具有这种目录/文件布局:

target/dist
target/dist/favicon.ico
target/dist/index.html
target/dist/static
target/dist/static/css
target/dist/static/css/chunk-vendors.0f1ada3b.css
target/dist/static/css/app.b6011a27.css
target/dist/static/js
target/dist/static/js/app.74994c71.js.map
target/dist/static/js/chunk-vendors.377aa5d2.js
target/dist/static/js/chunk-vendors.377aa5d2.js.map
target/dist/static/js/app.74994c71.js
target/dist/static/img
target/dist/static/img/logo.82b9c7a5.png

总之,我相信内置的javascript调试器运行良好,但存在一个配置错误,导致Visual Studio Code中的vueJS应用程序出现Unbound Breakpoints。

你看到问题了吗?

launch.json属性sourceMapPathOverrides的VS代码文档很少。特别是,我找不到映射重写的任何语法规则。然而,根据前面的评论,访问Debug: Diagnose Breakpoint Problems提供的面板中的VS代码链接确实会带来有用的解释和提示。因此,我能够发现错误的映射,目前,我已经通过以下方式解决了它们:

"sourceMapPathOverrides": {
"webpack:///./node_modules": "${webRoot}/node_modules",
"webpack:///./src/*": "${webRoot}/src/*"
}

我的整个launch.json现在显示:

{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"name": "vuejs: chrome",
"request": "launch",
"url": "http://localhost:8080/myapp",
"breakOnLoad": true,
"webRoot": "${workspaceFolder}/frontend",
"pathMapping": {
"/_karma_webpack_": "${workspaceFolder}/frontend"
},
"outFiles": ["${workspaceFolder}/frontend/target/dist/**/*.js"],
"vueComponentPaths": [
"${workspaceFolder}/frontend/src/**/*.vue"
],
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///./node_modules": "${webRoot}/node_modules",
"webpack:///./src/*": "${webRoot}/src/*"
}
}
]
}

有了这些映射,我至少能够让JS调试器";负载中断";。然而,源代码行与浏览器端相关的问题仍然存在,缩小了JS逻辑。为了解决这个问题,我将这里概述的逻辑合并到vue.config.js:中

const { SourceMapConsumer, SourceMapGenerator } = require('source-map');
const sourceMaps = {};
module.exports = {
// Change build paths to make them Maven compatible; see https://cli.vuejs.org/config/.
outputDir: 'target/dist',
assetsDir: 'static',
publicPath: '/myapp',
configureWebpack() {
return {
devtool: 'source-map',
plugins: [{
apply(compiler) {
compiler.hooks.thisCompilation.tap('Initializing Compilation', (compilation) => {
compilation.hooks.succeedModule.tap('Module Built', (module) => {
const { resource } = module;
if (!resource) return;
if (/node_modules/.test(resource)) return;
if (!/.vue/.test(resource)) return;
if (!/type=template/.test(resource)) return;
if (!module['_source'] || !module['_source']['_sourceMap']) return;
const pathWithoutQuery = module.resource.replace(/?.*$/, '');
sourceMaps[pathWithoutQuery] = module['_source']['_sourceMap'];
});
compilation.hooks.finishModules.tapPromise('All Modules Built', async (modules) => {
for (const module of modules) {
const { resource } = module;
if (!resource) continue;
if (/node_modules/.test(resource)) continue;
if (!/.vue/.test(resource)) continue;
if (!/type=script/.test(resource)) continue;
if (!/lang=ts/.test(resource)) continue;
if (!module['_source'] || !module['_source']['_sourceMap']) continue;
const pathWithoutQuery = module.resource.replace(/?.*$/, '');
const templateSourceMap = sourceMaps[pathWithoutQuery];
if (!templateSourceMap) continue;
const scriptSourceMap = module['_source']['_sourceMap'];
scriptSourceMap.sourcesContent = [...templateSourceMap.sourcesContent];
scriptSourceMap.sources = [...templateSourceMap.sources];
const lines = (templateSourceMap.sourcesContent[0] || '').match(/.+/g);
let indexOfScriptTag = 0;
for (const line of lines) {
++indexOfScriptTag;
if (/<script/.test(line)) break;
}
const shiftedSourceMap = await SourceMapConsumer.with(scriptSourceMap, null, async (consumer) => {
const generator = new SourceMapGenerator();
await consumer.eachMapping((mapping) => {
const {
generatedColumn,
generatedLine,
originalColumn,
originalLine
} = mapping;
let name = mapping.name;
let source = templateSourceMap.sources[0] || 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();
});
scriptSourceMap.mappings = shiftedSourceMap.mappings;
}
});
});
}
}]
};
}
}

有了这个设置,我可以从VS Code启动pwa-chrome调试器,并在IDE中逐步完成vueJS组件逻辑。

尽管如此,我还是决定,这种特定的技术组合——即(I)vscode js调试器,(ii)vueJS 3.x,以及(iii)vueJS3.x的TypeScript插件4.1.x——并不是通过IDE调试客户端javascript的最佳支持环境。

最新更新