^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
前置知识:
- sourceURL: 当前代码在source中的路径
- sourceMappingURL:当前代码映射到源码的sourceMap内容
debugger原理:
- 源码打了一个断点,然后通过 CDP 调试协议,将断点位置发送给浏览器
- 浏览器运行的文件是打包后的bundle.js,通过sourcemap找到断点位置
所以debugger是一通过sourcemap找文件的过程
源码如下:
// src/index.js
function add(a, b) {
return a + b
}
function multiple(a, b) {
return a * b
}
var firstOp = 9
var secondOp = 10
add(firstOp, secondOp)
eval
eval("function add(a, b) {\n return a + b\n}\nfunction multiple(a, b) {\n return a * b\n}\nvar firstOp = 9\nvar secondOp = 10\nadd(firstOp, secondOp)\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
eval在浏览器source中新建一个文件,文件地址就是webpack://webpack-demo/./src/index.js
source-map
// bundle.js
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
function add(a, b) {
return a + b
}
function multiple(a, b) {
return a * b
}
var firstOp = 9
var secondOp = 10
add(firstOp, secondOp)
/******/ })()
;
//# sourceMappingURL=bundle.js.map
// bundle.js.map
{"version":3,"file":"bundle.js","mappings":";;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sB","sources":["webpack://webpack-demo/./src/index.js"],"sourcesContent":["function add(a, b) {\n return a + b\n}\nfunction multiple(a, b) {\n return a * b\n}\nvar firstOp = 9\nvar secondOp = 10\nadd(firstOp, secondOp)"],"names":[],"sourceRoot":""}
sourceMappingURL指向的sourcemap包含行和列,还有源码sourcesContent,内容比较大。sourcesContent存储源码是为了当找不到源码文件的时候还能进行断点和报错,如果源码保证能找到的话,可以不需要sourcesContent字段
eval-source-map
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/ (() => {
eval("function add(a, b) {\n return a + b\n}\nfunction multiple(a, b) {\n return a * b\n}\nvar firstOp = 9\nvar secondOp = 10\nadd(firstOp, secondOp)//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvaW5kZXguanMuanMiLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vd2VicGFjay1kZW1vLy4vc3JjL2luZGV4LmpzP2I2MzUiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gYWRkKGEsIGIpIHtcbiAgcmV0dXJuIGEgKyBiXG59XG5mdW5jdGlvbiBtdWx0aXBsZShhLCBiKSB7XG4gIHJldHVybiBhICogYlxufVxudmFyIGZpcnN0T3AgPSA5XG52YXIgc2Vjb25kT3AgPSAxMFxuYWRkKGZpcnN0T3AsIHNlY29uZE9wKSJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/index.js\n");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval-source-map devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./src/index.js"]();
/******/
/******/ })()
;
eval会产生两个sourceURL,一个是[module],第二个是具体的地址,后面的会覆盖前面的(不是很懂为什么要有sourceURL=[module])
eval-source-map有关的debugger需要特别注意
// 这是上面的sourceMappingURL解码
{
"version":3,
"file":"./src/index.js.js",
"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA",
"sources":**[
"webpack://webpack-demo/./src/index.js?b635"
],
"sourcesContent":**[
"function add(a, b) {\n return a + b\n}\nfunction multiple(a, b) {\n return a * b\n}\nvar firstOp = 9\nvar secondOp = 10\nadd(firstOp, secondOp)"
],
"names":**[
],
"sourceRoot":""
}
sources里面的路径会带hash值,造成本地找不到源码,所以浏览器打不了断点,除非手动debugger
inline
// bundle.js
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
function add(a, b) {
return a + b
}
function multiple(a, b) {
return a * b
}
var firstOp = 9
var secondOp = 10
add(firstOp, secondOp)
/******/ })()
;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vd2VicGFjay1kZW1vLy4vc3JjL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIGFkZChhLCBiKSB7XG4gIHJldHVybiBhICsgYlxufVxuZnVuY3Rpb24gbXVsdGlwbGUoYSwgYikge1xuICByZXR1cm4gYSAqIGJcbn1cbnZhciBmaXJzdE9wID0gOVxudmFyIHNlY29uZE9wID0gMTBcbmFkZChmaXJzdE9wLCBzZWNvbmRPcCkiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=
就是把sourcemap以base64编码直接写在打包文件的最后
hidden
// bundle.js
/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
function add(a, b) {
return a + b
}
function multiple(a, b) {
return a * b
}
var firstOp = 9
var secondOp = 10
add(firstOp, secondOp)
/******/ })()
;
打包文件没有sourcemap链接,但还是生成了sourcemap文件
nosources
{"version":3,"file":"bundle.js","mappings":";;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sB","sources":["webpack://webpack-demo/./src/index.js"],"names":[],"sourceRoot":""}
去掉了sourcesContent,体积精简了很多,需要确保能根据sourcemap找到源文件
cheap
{"version":3,"file":"bundle.js","mappings":";;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":["webpack://webpack-demo/./src/index.js"],"sourcesContent":["function add(a, b) {\n return a + b\n}\nfunction multiple(a, b) {\n return a * b\n}\nvar firstOp = 9\nvar secondOp = 10\nadd(firstOp, secondOp)"],"names":[],"sourceRoot":""}
sourcemap信息减少到行(mappings长度有所减少)
cheap打断点的时候也需要注意,它发送的断点路径非常简略,会打到别的位置上,若是本地调试,不推荐用cheap
module
源码通过loader会改动,module就是从源码经过每个loader都会做一层映射,所以能找到源码,不然就只能找到打包前那一个状态的源码
debugger实战:
vue-cli
eval-cheap-module-source-map
断点位置:vue-demo1/src/App.vue
webpack需要一层映射,将debugger的地方加一个webpack://发送给浏览器
"sourceMapPathOverrides": {
"meteor://💻app/*": "${workspaceFolder}/*",
"webpack:///./~/*": "${workspaceFolder}/node_modules/*",
"webpack://?:*/*": "${workspaceFolder}/*"
}
所以浏览器接收到的断点位置是:webpack://vue-demo1/src/App.vue
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "render": function() { return /* binding */ render; }
/* harmony export */ });
/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ "./node_modules/vue/dist/vue.runtime.esm-bundler.js");
/* harmony import */ var _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./assets/logo.png */ "./src/assets/logo.png");
const _hoisted_1 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)("img", {
alt: "Vue logo",
src: _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__
}, null, -1 /* HOISTED */);
function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_HelloWorld = (0,vue__WEBPACK_IMPORTED_MODULE_0__.resolveComponent)("HelloWorld");
return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)(vue__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, [_hoisted_1, (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_HelloWorld, {
msg: "Welcome to Your Vue.js App"
})], 64 /* STABLE_FRAGMENT */);
}//# sourceURL=[module]
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvYmFiZWwtbG9hZGVyL2xpYi9pbmRleC5qcz8/Y2xvbmVkUnVsZVNldC00MC51c2VbMF0hLi9ub2RlX21vZHVsZXMvdnVlLWxvYWRlci9kaXN0L3RlbXBsYXRlTG9hZGVyLmpzPz9ydWxlU2V0WzFdLnJ1bGVzWzNdIS4vbm9kZV9tb2R1bGVzL3Z1ZS1sb2FkZXIvZGlzdC9pbmRleC5qcz8/cnVsZVNldFswXS51c2VbMF0hLi9zcmMvQXBwLnZ1ZT92dWUmdHlwZT10ZW1wbGF0ZSZpZD03YmE1YmQ5MC5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7O0FBQzZDO2dDQUEzQ0MsdURBQUFBLENBQTRDO0VBQXZDQyxHQUFHLEVBQUMsVUFBVTtFQUFDQyxHQUF1QixFQUF2QkgsNkNBQUFBOzs7OztxS0FBcEJJLFVBQTRDLEVBQzVDQyxnREFBQUEsQ0FBOENDO0lBQWxDQyxHQUFHLEVBQUM7RUFBNEIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly92dWUtZGVtbzEvLi9zcmMvQXBwLnZ1ZT85MWEwIl0sInNvdXJjZXNDb250ZW50IjpbIjx0ZW1wbGF0ZT5cbiAgPGltZyBhbHQ9XCJWdWUgbG9nb1wiIHNyYz1cIi4vYXNzZXRzL2xvZ28ucG5nXCI+XG4gIDxIZWxsb1dvcmxkIG1zZz1cIldlbGNvbWUgdG8gWW91ciBWdWUuanMgQXBwXCIvPlxuPC90ZW1wbGF0ZT5cblxuPHNjcmlwdD5cbmltcG9ydCBIZWxsb1dvcmxkIGZyb20gJy4vY29tcG9uZW50cy9IZWxsb1dvcmxkLnZ1ZSdcblxuZXhwb3J0IGRlZmF1bHQge1xuICBuYW1lOiAnQXBwJyxcbiAgY29tcG9uZW50czoge1xuICAgIEhlbGxvV29ybGRcbiAgfSxcbiAgbW91bnRlZCgpIHtcbiAgICBkZWJ1Z2dlclxuICB9XG59XG48L3NjcmlwdD5cblxuPHN0eWxlPlxuI2FwcCB7XG4gIGZvbnQtZmFtaWx5OiBBdmVuaXIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gIGNvbG9yOiAjMmMzZTUwO1xuICBtYXJnaW4tdG9wOiA2MHB4O1xufVxuPC9zdHlsZT5cbiJdLCJuYW1lcyI6WyJfaW1wb3J0c18wIiwiX2NyZWF0ZUVsZW1lbnRWTm9kZSIsImFsdCIsInNyYyIsIl9ob2lzdGVkXzEiLCJfY3JlYXRlVk5vZGUiLCJfY29tcG9uZW50X0hlbGxvV29ybGQiLCJtc2ciXSwic291cmNlUm9vdCI6IiJ9
//# sourceURL=webpack-internal:///./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90
sourceMappingURL解析
**{
"version":3,
"file":"./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90.js",
"mappings":";;;;;;;AAC6C;gCAA3CC,uDAAAA,CAA4C;EAAvCC,GAAG,EAAC,UAAU;EAACC,GAAuB,EAAvBH,6CAAAA;;;;;qKAApBI,UAA4C,EAC5CC,gDAAAA,CAA8CC;IAAlCC,GAAG,EAAC;EAA4B",
"sources":**[
"webpack://vue-demo1/./src/App.vue?91a0"
],
"sourcesContent":**[
"<template>\n <img alt=\"Vue logo\" src=\"./assets/logo.png\">\n <HelloWorld msg=\"Welcome to Your Vue.js App\"/>\n</template>\n\n<script>\nimport HelloWorld from './components/HelloWorld.vue'\n\nexport default {\n name: 'App',\n components: {\n HelloWorld\n },\n mounted() {\n debugger\n }\n}\n</script>\n\n<style>\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n</style>\n"
],
"names":**[
"_imports_0",
"_createElementVNode",
"alt",
"src",
"_hoisted_1",
"_createVNode",
"_component_HelloWorld",
"msg"
],
"sourceRoot":""
}
由于传递的断点位置和sourcemap的不匹配(有hash值),所以断点不生效,若是想要断点生效,需要改变sourcemap的模式(不用eval)
vite
vite都是绝对地址,在源码打了断点后,断点发送给浏览器,但是vite有多个热更新文件,断点可能会打在同路径的热更新文件上
断点位置: vue-demo2\src\components\HelloWorld.vue
相对路径: src\components\HelloWorld
例如
http://localhost:5173/src/components/HelloWorld.vue?vue&type=style&index=0&scoped=e17ea971&lang.css
http://localhost:5173/src/components/HelloWorld.vue
这两个文件都收到了断点,但是他们不是源码
**{
"version":3,
"names":**[
],
"sources":**[
"C:/Users/mjgao/vue-demo2/src/components/HelloWorld.vue"
],
"sourcesContent":**[
"<script setup>\nimport { reactive, ref } from 'vue'\nlet a = ref(1)\ndefineProps({\n msg: {\n type: String,\n required: true,\n },\n})\n\nconst me = reactive({\n name: 'gmj',\n})\n</script>\n\n<template>\n <div class=\"greetings\">\n <h1 class=\"green\">{{ msg }}</h1>\n <h3>\n You’ve successfully created a project with\n <a href=\"https://vitejs.dev/\" target=\"_blank\" rel=\"noopener\">Vite</a> +\n <a href=\"https://vuejs.org/\" target=\"_blank\" rel=\"noopener\">Vue 3</a>.\n </h3>\n </div>\n</template>\n\n<style scoped>\nh1 {\n font-weight: 500;\n font-size: 2.6rem;\n top: -10px;\n}\n\nh3 {\n font-size: 1.2rem;\n}\n\n.greetings h1,\n.greetings h3 {\n text-align: center;\n}\n\n@media (min-width: 1024px) {\n .greetings h1,\n .greetings h3 {\n text-align: left;\n }\n}\n</style>\n"
],
"mappings":"AACA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;;AADrB;AAEd,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAMZ;AACF;AACA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACd,CAAC,CAAC;;;;;;;;;;;qBAIK,KAAK,EAAC,WAAW;qBAChB,KAAK,EAAC,OAAO;iEACjB,oBAIK;gCAJD,8CAEF;eAAA,oBAAqE;IAAlE,IAAI,EAAC,qBAAqB;IAAC,MAAM,EAAC,QAAQ;IAAC,GAAG,EAAC,UAAU;KAAC,MAAI;gCAAI,KACrE;eAAA,oBAAqE;IAAlE,IAAI,EAAC,oBAAoB;IAAC,MAAM,EAAC,QAAQ;IAAC,GAAG,EAAC,UAAU;KAAC,OAAK;gCAAI,IACvE;;;;wBANF,oBAOM,OAPN,UAOM;IANJ,oBAAgC,MAAhC,UAAgC,mBAAX,UAAG;IACxB,UAIK"
}
如果把webRoot改写成这样"webRoot": "${workspaceFolder}/aaa",
这时候相对路径就是aaa\src\components\HelloWorld,但是绝对路径没有变,所以能够忽略掉热更新文件以及没有采用绝对路径的文件,例如main.js打的断点就没有用了
这里mac和win的系统还是有点区别