source-map从入门到放弃和sentry检测

1,149 阅读1分钟

我正在参加「掘金·启航计划」

本文主要说两个主题:

  • 1、webpack的source-map理解

  • 2、source-map文件原理

写本文的时候,居然发现了webpack5的bug

发现webpack@5.74.0版本eval-nosources-的sourcesContent没置空bug

发现 eval-nosources- 开头的三个配置没起作用,sourcesContent还有源码

EvalSourceMapDevToolPlugin.js同SourceMapDevToolPlugin.js对比,应该在红圈位置加上

if (options.noSources) {
    sourceMap.sourcesContent = undefined;
}

image.png

image.png

言归正传 webpack的devtool说明

webpack的devtool配置souce-map官方类型有24+1种

是按照这个正则来校验的:^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$

devtool需要从右往左阅读来理解。

**先说结论,字符串包括关键字:**

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-module-:去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • cheap-:只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • nosources-:.map的sourcesContent内容空

  • inline-:去掉.map文件,sourceMappingURL改为.map的base64内容

  • eval- :所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

  • hidden-:去掉js中sourceMappingURL字段,就是不关联js和map文件

关于module段特别解释

webpack 中对一个模块会进行多次处理,比如经过 loader A 做一次转换,再用 loader B 做一次转换,之后打包到一起。

每次转换都会生成 sourcemap,那也就是有多个 sourcemap:

默认 sourcemap 只是能从 bundle 关联到模块的代码,也就是只关联了最后那个 sourcemap。

那如果你想调试最初的源码怎么办呢?

那就把每一次的 loader 的 sourcemap 也关联起来,这就是 module 配置的作用。这样就能一次性映射回最初的源码:

当你想调试最初的源码的时候,module 的配置就很有用了。

webpack中各种devtool配置效果

我们webpack有babel-loader,下面列出实际打包出的效果

源码文件

const fn = (b) => console.log(b, a);
fn(11,a);

none

console.log(11);

image.png

eval

结论:

  • 没有source-map,就没有.map文件

  • eval,所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);\n\n//# sourceURL=webpack://webpack/./src/index.js?"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

eval-cheap-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • eval,所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzP2Q5YmUiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGZuID0gZnVuY3Rpb24gZm4oYikge1xuICByZXR1cm4gY29uc29sZS5sb2coYiwgYSk7XG59O1xuXG5mbigxMSk7Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

sourceMappingURL后面的base64字符串可通过btoa直接解码

image.png

eval-cheap-module-source-map:

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 有cheap-module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • eval,所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibmFtZXMiOlsiZm4iLCJiIiwiY29uc29sZSIsImxvZyIsImEiXSwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2VicGFjazovL3dlYnBhY2svLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBmbiA9IChiKSA9PiBjb25zb2xlLmxvZyhiLCBhKTtcclxuZm4oMTEpO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBLElBQU1BLEVBQUUsR0FBRyxTQUFMQSxFQUFLLENBQUNDLENBQUQ7RUFBQSxPQUFPQyxPQUFPLENBQUNDLEdBQVIsQ0FBWUYsQ0FBWixFQUFlRyxDQUFmLENBQVA7QUFBQSxDQUFYOztBQUNBSixFQUFFLENBQUMsRUFBRCxDQUFGIn0=\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

eval-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • eval,所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibmFtZXMiOlsiZm4iLCJiIiwiY29uc29sZSIsImxvZyIsImEiXSwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2VicGFjazovL3dlYnBhY2svLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBmbiA9IChiKSA9PiBjb25zb2xlLmxvZyhiLCBhKTtcclxuZm4oMTEpO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBLElBQU1BLEVBQUUsR0FBRyxTQUFMQSxFQUFLLENBQUNDLENBQUQ7RUFBQSxPQUFPQyxPQUFPLENBQUNDLEdBQVIsQ0FBWUYsQ0FBWixFQUFlRyxDQUFmLENBQVA7QUFBQSxDQUFYOztBQUNBSixFQUFFLENBQUMsRUFBRCxDQUFGIn0=\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

cheap-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

console.log(11,a);
//# sourceMappingURL=index.5ea4e4b4b5eaeba4e7de.js.map

image.png

cheap-module-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

console.log(11,a);
//# sourceMappingURL=index.e522dfeb5199effa0e93.js.map

image.png

source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径
console.log(11,a);
//# sourceMappingURL=index.5ea4e4b4b5eaeba4e7de.js.map

image.png

inline-cheap-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • 有inline,去掉.map文件,sourceMappingURL改为.map的base64内容

console.log(11, a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguNWVhNGU0YjRiNWVhZWJhNGU3ZGUuanMiLCJtYXBwaW5ncyI6IkFBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbInZhciBmbiA9IGZ1bmN0aW9uIGZuKGIpIHtcbiAgcmV0dXJuIGNvbnNvbGUubG9nKGIsIGEpO1xufTtcblxuZm4oMTEpOyJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==

image.png

inline-cheap-module-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 有cheap-module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • 有inline,去掉.map文件,sourceMappingURL改为.map的base64内容

console.log(11,a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZTUyMmRmZWI1MTk5ZWZmYTBlOTMuanMiLCJtYXBwaW5ncyI6IkFBQUEiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IGZuID0gKGIpID0+IGNvbnNvbGUubG9nKGIsIGEpO1xyXG5mbigxMSk7XHJcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==

image.png

inline-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • inline,js文件的生成sourceMappingURL改为base64编码.map文件,通过.map文件的sources字段还原.map文件名字

console.log(11,a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguM2MyMDY5YTkzODFhNDMwYzVjMzguanMiLCJtYXBwaW5ncyI6IkFBQ2tCQSxRQUFRQyxJQUN2QixHQUQ4QkMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIGltcG9ydCBhIGZyb20gXCIuL2JcIjtcclxuY29uc3QgZm4gPSAoYikgPT4gY29uc29sZS5sb2coYiwgYSk7XHJcbmZuKDExKTtcclxuLy8gY2xhc3MgQSB7XHJcbi8vICAgY29uc3RydWN0b3IoKSB7fVxyXG4vLyB9XHJcbi8vIG5ldyBBKCk7XHJcbiJdLCJuYW1lcyI6WyJjb25zb2xlIiwibG9nIiwiYSJdLCJzb3VyY2VSb290IjoiIn0=

image.png

eval-nosources-cheap-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • eval,所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzP2Q5YmUiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGZuID0gZnVuY3Rpb24gZm4oYikge1xuICByZXR1cm4gY29uc29sZS5sb2coYiwgYSk7XG59O1xuXG5mbigxMSk7Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

eval-nosources-cheap-module-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • 有cheap-module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • nosources-:.map的sourcesContent内容空

  • eval- :所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibmFtZXMiOlsiZm4iLCJiIiwiY29uc29sZSIsImxvZyIsImEiXSwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2VicGFjazovL3dlYnBhY2svLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBmbiA9IChiKSA9PiBjb25zb2xlLmxvZyhiLCBhKTtcclxuZm4oMTEpO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBLElBQU1BLEVBQUUsR0FBRyxTQUFMQSxFQUFLLENBQUNDLENBQUQ7RUFBQSxPQUFPQyxPQUFPLENBQUNDLEdBQVIsQ0FBWUYsQ0FBWixFQUFlRyxDQUFmLENBQVA7QUFBQSxDQUFYOztBQUNBSixFQUFFLENBQUMsRUFBRCxDQUFGIn0=\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

eval-nosources-source-map

从命名右往左读,结论:

  • source-map结尾,生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • nosources-:.map的sourcesContent内容空

  • eval- :所有代码用eval包裹,生成sourceMap映射到eval内容独立成文件,sourceMappingURL执行.map文件的base64内容

(function () {
  var __webpack_modules__ = {
      579: function () {
        eval(
          "var fn = function fn(b) {\n  return console.log(b, a);\n};\n\nfn(11);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNTc5LmpzIiwibmFtZXMiOlsiZm4iLCJiIiwiY29uc29sZSIsImxvZyIsImEiXSwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2VicGFjazovL3dlYnBhY2svLi9zcmMvaW5kZXguanM/YjYzNSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBmbiA9IChiKSA9PiBjb25zb2xlLmxvZyhiLCBhKTtcclxuZm4oMTEpO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBLElBQU1BLEVBQUUsR0FBRyxTQUFMQSxFQUFLLENBQUNDLENBQUQ7RUFBQSxPQUFPQyxPQUFPLENBQUNDLEdBQVIsQ0FBWUYsQ0FBWixFQUFlRyxDQUFmLENBQVA7QUFBQSxDQUFYOztBQUNBSixFQUFFLENBQUMsRUFBRCxDQUFGIn0=\n//# sourceURL=webpack-internal:///579\n"
        );
      },
    },
    __webpack_exports__ = {};
  __webpack_modules__[579]();
})();

image.png

image.png

inline-nosources-cheap-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-:只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • nosources-:.map的sourcesContent内容空

  • inline-:去掉.map文件,sourceMappingURL改为.map的base64内容

console.log(11,a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguNWVhNGU0YjRiNWVhZWJhNGU3ZGUuanMiLCJtYXBwaW5ncyI6IkFBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrLy4vc3JjL2luZGV4LmpzIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9

image.png

inline-nosources-cheap-module-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-module-:去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • nosources-:.map的sourcesContent内容空

  • inline-:去掉.map文件,sourceMappingURL改为.map的base64内容

console.log(11,a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZXMiOlsid2VicGFjazovL3Rlc3Qtd2VicGFjazUvLi9zcmMvaW5kZXguanMiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=

image.png

inline-nosources-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • nosources-:.map的sourcesContent内容空

  • inline-:去掉.map文件,sourceMappingURL改为.map的base64内容

console.log(11,a);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsIm1hcHBpbmdzIjoiQUFBa0JBLFFBQVFDLElBQ3ZCLEdBRDhCQyIsInNvdXJjZXMiOlsid2VicGFjazovL3Rlc3Qtd2VicGFjazUvLi9zcmMvaW5kZXguanMiXSwibmFtZXMiOlsiY29uc29sZSIsImxvZyIsImEiXSwic291cmNlUm9vdCI6IiJ9

image.png

nosources-cheap-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-:只有cheap没有module,去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行;没有module,sourcesContent内容是loader编译后内容

  • nosources-:.map的sourcesContent内容空

main.js

console.log(11,a);
//# sourceMappingURL=main.js.map

main.js.map

{"version":3,"file":"main.js","mappings":"AACA","sources":["webpack://test-webpack5/./src/index.js"],"names":[],"sourceRoot":""}

image.png

nosources-cheap-module-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • cheap-module-:去掉.map文件中mapping的VLQ编码的第四个即列信息,所以只显示到行

  • nosources-:.map的sourcesContent内容空

main.js

console.log(11,a);
//# sourceMappingURL=main.js.map

main.js.map

{"version":3,"file":"main.js","mappings":"AAAA","sources":["webpack://test-webpack5/./src/index.js"],"names":[],"sourceRoot":""}

image.png

hidden-nosources-source-map

从命名右往左读,结论:

  • -source-map : 生成独立又完整的.map文件(mapping中包含行和列即错误信息可显示到行列,有sourcesContent字段是最初源代码内容),js文件的sourceMappingURL指向.map文件路径

  • nosources-:.map的sourcesContent内容空

  • hidden-:去掉js中sourceMappingURL字段,就是不关联js和map文件

main.js

console.log(11,a);

main.js.map

{"version":3,"file":"main.js","mappings":"AAAkBA,QAAQC,IACvB,GAD8BC","sources":["webpack://test-webpack5/./src/index.js"],"names":["console","log","a"],"sourceRoot":""}

image.png

其他就不一一说明

  • nosources-source-map

  • hidden-nosources-cheap-source-map

  • hidden-nosources-cheap-module-source-map

  • hidden-cheap-source-map

  • hidden-cheap-module-source-map

  • hidden-source-map

source-map的.map文件说明

参考官网定义

docs.google.com/document/d/…

learn.microsoft.com/zh-cn/archi…

source map 版本3

目前source map最新版本是2013年推出的版本3

目标

  • 减少整体大小以改善解析时间、内存消耗和下载时间。

  • 支持源级调试,允许双向映射

  • 支持服务器端堆栈跟踪去混淆

格式

{
    "version" : 3,
    "file": "out.js",
    "sourceRoot": "",
    "sources": ["foo.js", "bar.js"],
    "sourcesContent": [null, null],
    "names": ["src", "maps", "are", "fun"],
    "mappings": "A,AAAB;;ABCDE;"
}

规范标准原文的解释

  • json开始 Line 1: The entire file is a single JSON object

  • version必须,版本号必须入口字段也必须是正整数,现在都是3 Line 2: File version (always the first entry in the object) and must be a positive integer.

  • file可选,该.map文件对应的关联js文件 Line 3: An optional name of the generated code that this source map is associated with.

  • sourceRoot可选,源文件根目录 Line 4: An optional source root, useful for relocating source files on a server or removing repeated values in the “sources” entry.  This value is prepended to the individual entries in the “source” field.

  • sources必须,源文件文件名列表 Line 5: A list of original sources used by the “mappings” entry.

  • sourcesContent可选,源代码内容列表 Line 6: An optional list of source content, useful when the “source” can’t be hosted. The contents are listed in the same order as the sources in line 5. “null” may be used if some original sources should be retrieved by name.

  • names必须,符号名称列表 Line 7: A list of symbol names used by the “mappings” entry.

  • mappings必须,VLQ编码 Line 8: A string with the encoded mapping data.

原文特别说明mappings字段

The “mappings” data is broken down as follows:

  • 用分号分组,每组代表生成代码的一行 each group representing a line in the generated file is separated by a ”;”

  • 用逗号分隔,每组代表一个分词 each segment is separated by a “,”

  • 每个分词用1、4或者5变长VLQ组成 each segment is made up of 1,4 or 5 variable length fields.

mapppings中VLQ组成解释

  1. 段表示的生成代码中从零开始的行的起始列。如果这是第一个段的第一个字段,或者是新生成的行 (“;”) 之后的第一个段,则该字段保存整个 64 基 VLQ。否则,该字段包含一个相对于该字段先前出现的base 64 VLQ。请注意,这与下面的字段不同,因为在每个生成的行之后都会重置先前的值。 The zero-based starting column of the line in the generated code that the segment represents. If this is the first field of the first segment, or the first segment following a new generated line (“;”), then this field holds the whole base 64 VLQ. Otherwise, this field contains a base 64 VLQ that is relative to the previous occurrence of this field. Note that this is different than the fields below because the previous value is reset after every generated line.

  2. 如果存在,“源”列表中的从零开始的索引。该字段是相对于该字段的前一次出现的基数为 64 的 VLQ,除非这是该字段的第一次出现,在这种情况下表示整个值。If present, an zero-based index into the “sources” list. This field is a base 64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented.

  3. 如果存在,则表示原始源中从零开始的起始行。该字段是相对于该字段的前一次出现的基数为 64 的 VLQ,除非这是该字段的第一次出现,在这种情况下表示整个值。如果有源字段,则始终存在。 If present, the zero-based starting line in the original source represented. This field is a base 64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented. Always present if there is a source field.

  4. 如果存在,则表示源中行的从零开始的列。该字段是相对于该字段的前一次出现的基数为 64 的 VLQ,除非这是该字段的第一次出现,在这种情况下表示整个值。如果有源字段,则始终存在。If present, the zero-based starting column of the line in the source represented. This field is a base 64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented. Always present if there is a source field.

  5. 如果存在,则从零开始索引到与此段关联的“名称”列表中。该字段是相对于该字段的前一次出现的基数为 64 的 VLQ,除非这是该字段的第一次出现,在这种情况下表示整个值。注意:在使用 Google 日历执行的测试中,这种编码相对于 V2 格式将源地图大小减少了 50%。If present, the zero-based index into the “names” list associated with this segment. This field is a base 64 VLQ relative to the previous occurrence of this field, unless this is the first occurrence of this field, in which case the whole value is represented.
    Note: This encoding reduces the source map size 50% relative to the V2 format in tests performed using Google Calendar.

VLQ计算例子

image.png

AAgBC  
// 先将每一位按 base64 编码还原为 6 位二进制字符串  
=> 000000 000000 100000 000001 000010  
  
// 然后进行分组,最高位的含义是是否连续,如果是 1 则连续,所以要和后面的放到一组  
=> (000000) (000000) (100000 000001) (000010)  
  
  
// 分完组之后,最高位就可以移除了,由于 VLQ 只能表示 -15 ~ 15 之间的数,所以有的数值是切割之后来表示的,所以我们要先还原它,去除最高位倒序拼接即可  
=> (00000) (00000) (0000100000) (00010)  
  
  
// 之后将最低位的符号位去除,虽然 VLQ 可以表示正负,但代码中没有负行负列这么一说,通常都是 0,所以直接去掉就行了  
=> (0000) (0000) (000010000) (0001)  
  
  
// 最后还原为十进制数  
=> 0 0 16 1

sentry 前端异常检查

开发环境用 eval-source-map

发布环境用 hidden-nosources-source-map

第三方检测

弃用 fundebug 因为上传会失败

image.png

注册sentry账号或者用docker私有化安装

我这里直接用的官网,私有化安装直接 dockerhub上找官网镜像就有安装教程

sentry.io/

注册账号后添加项目

比如我是vue项目 docs.sentry.io/platforms/j…

把项目初始化内容直接粘贴过来,注意放到new vue上面

image.png

再添加apikey,注意勾选project write权限

sentry.io/settings/ac…

image.png

npm i @sentry/tracing @sentry/vue
npm i -D @sentry/webpack-plugin

webpack配置

const webpack = require("webpack");
const { defineConfig } = require("@vue/cli-service");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");

module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  devServer: {
    proxy: {
      "/api": {
        target: "https://99haoyun.com",
        changeOrigin: true,
      },
    },
  },
  parallel: false, // 解决 SentryWebpackPlugin报错releasePromise.then is not a function
  configureWebpack: (config) => {
    config.plugins.push(
      new webpack.DefinePlugin({
        "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
        "process.env.BUILD": "'build'",
      })
    );

    if (process.env.NODE_ENV === "production") {
      config.devtool = "hidden-source-map";
      config.plugins.push(
        new SentryWebpackPlugin({
          include: "./dist",
          ignoreFile: ".gitignore",
          ignore: ["node_modules", "webpack.config.js"],
          configFile: "./.sentryclirc",
        })
      );
    }
  },
});

main.js配置

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";

import install from "./install";

import * as Sentry from "@sentry/vue";
import { BrowserTracing } from "@sentry/tracing";

Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(install);

if (process.env.NODE_ENV == "production") {
  Sentry.init({
    Vue,
    dsn: "https://388d8cbd62394bd59aac2a0f40b710@o45040265451274.ingest.sentry.io/45040265479936",
    integrations: [
      new BrowserTracing({
        routingInstrumentation: Sentry.vueRouterInstrumentation(router),
        tracingOrigins: ["localhost", "my-site-url.com", /^\//],
      }),
    ],
    // Set tracesSampleRate to 1.0 to capture 100%
    // of transactions for performance monitoring.
    // We recommend adjusting this value in production
    tracesSampleRate: 1.0,
  });
}

window.$winvue = new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");

清除打包后map文件

我在dockerfile里面build之后调用 RUN npm run gulp-cleanmap,package.json增加scripts "gulp-cleanmap": "gulp cleanmap"

const { exec } = require("child_process");
const gulp = require("gulp");
const rimraf = require("rimraf");

gulp.task("cleanmap", function (cb) {
  rimraf("dist/**/*.map", function (err, stdout, stderr) {
    console.log(err, stdout, stderr);
    cb();
  });
});

根目录增加.sentryclirc

这里token是上面的apikey

[auth]
token=083e269a54ed400c989b0eeee02c59109fc43b26ba4cff8c63e65ff7ec91

[defaults]
url=https://sentry.io/
org=rootegg
project=javascript-vue

解决报错Unable to download sentry-cli binary

# compile
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./

# 解决 Unable to download sentry-cli binary
RUN npm set ENTRYCLI_CDNURL=https://cdn.npm.taobao.org/dist/sentry-cli
RUN npm set sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli

RUN npm install --registry=https://registry.npm.taobao.org
COPY . .
RUN npm run build

# gulp 清理map文件
RUN npm run gulp-cleanmap

# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

效果

image.png