利用sourceMap定位错误实践

6,141 阅读3分钟

前言

本文记录了使用mozilla/source-map库来定位打包后的js代码错误,至于sourcemap文件的原理在此不再赘述,网上已经有很多很好的教程文章,比如阮一峰老师的JavaScript Source Map 详解与joeyguo的脚本错误量极致优化-让脚本错误一目了然

本文让我们在实践中加深对sourceMap的理解吧!(建议阅读以上两篇文章后再开始实践)

流程非常简单,如下:

  1. demo准备,并生成sourceMap
  2. 利用mozilla/source-map库处理指出出错源文件、出错源文件行列数、原变量名

demo都在如下GitHub地址:Joeoeoe/source-map-demo

demo准备,并生成sourceMap

配置一个简单的webpack:

//webpack.config.js
const path = require("path");

module.exports = {
  devtool: "source-map",
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
};

src文件夹下创建index.jsmoudleA.js

// moduleA.js
const moudleA = "moduleA";

module.exports = moudleA;
// index.js
const moudleA = require("./moduleA");
console.log(moudleA);

if (true) {
  console.log(a); // 模拟的错误
}

接下来用webpack打包,因为我们的webpack中配置了devtool: "source-map",所以会生成main.jsmain.js.map

// main.js,一串压缩后的代码,末尾指向对应的sourceMap
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};......
//# sourceMappingURL=main.js.map
// main.js.map sourceMap内容,让我们把他格式化出来看看吧
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/moduleA.js","webpack:///./src/index.js"]....

利用json格式化工具,看看sourceMap的内容:

sources数组下是转换前的文件,其内容与sourcesContent下标一一对应。通过查看sourcesContent,可以发现webpack/bootstrap即webpack在浏览器端对require的模拟。其他属性在阮一峰老师的文章里有很详细的解释,此处就不再赘述了。

demo准备完后接下来可以根据sourceMap定位错误了。

利用mozilla/source-map库定位错误

项目中利用sourceMap定位错误的方案一般如下(图自脚本错误量极致优化-让脚本错误一目了然):

为了模拟外网环境出错,把main.js中的**//# sourceMappingURL=main.js.map**去掉,在dist/index.html中添加错误监听:

    <script>
      window.onerror = function (msg, url, row, col, error) {
        const obj = {
          msg,
          url,
          row,
          col,
        };
        console.log(obj);
      };
    </script>

打开html可看见:

有了rowcol之后接下来使用mozilla/source-map库获取错误在源文件的位置。

npm i source-map安装source-map库,在项目下新建trySourceMap.js文件:

const fs = require("fs");
const sourceMap = require("source-map"); // mozilla/source-map库
const rawSourceMap = JSON.parse(
  // 打包后的sourceMap文件
  fs.readFileSync("./dist/main.js.map").toString()
);

const errorPos = {
  // 上图中的错误位置
  line: 1,
  column: 946,
};

async function main() {
  const consumer = await new sourceMap.SourceMapConsumer(rawSourceMap); // 获取sourceMap consumer,我们可以通过传入打包后的代码位置来查询源代码的位置

  const originalPosition = consumer.originalPositionFor({ // 获取 出错代码 在 哪一个源文件及其对应位置
    line: errorPos.line,
    column: errorPos.column,
  });
  // { source: 'webpack:///src/index.js', line: 4, column: 14, name: 'a' }

  // 根据源文件名寻找对应源文件
  const sourceIndex = consumer.sources.findIndex(
    (item) => item === originalPosition.source
  );
  const sourceContent = consumer.sourcesContent[sourceIndex];
  const contentRowArr = sourceContent.split("\n"); //切分
  // [
  //  'const moudleA = require("./moduleA");\r',
  //  '\r',
  //  'if (true) {\r',
  //  '  console.log(a);\r',
  //  '}\r',
  //  ''
  // ]

  // 接下来根据行和列可获取更加具体的位置
  console.log(contentRowArr[originalPosition.line - 1]);

  consumer.destroy(); // 使用完后记得destroy
}

main();

如此便捕捉到了出错代码在源文件的位置。

结语

文本记录了如何利用sourceMap定位线上代码的错误,完整demo均在此GitHub地址:Joeoeoe/source-map-demo

参考资料

JavaScript Source Map 详解——阮一峰
Introduction to JavaScript Source Maps
脚本错误量极致优化-让脚本错误一目了然
mozilla/source-map——Github
webpack是如何实现前端模块化的