通常情况下,为了减少请求数和文件下载体积,保证页面以最快的时间载入,线上文件的代码都是经过打包工具合并压缩过的。甚至有的时候,为了防止不法分子或者竞争对手来反编译你的源码,我们会进一步对代码进行混淆或者加密。

虽然,我们可以用很多方式来进行线上代码调试,比如通过 fiddler 或者 charles 相关工具进行抓包,然后代理到本地文件。但是对于一些混淆的、可读性差的代码,问题的定位还是有点麻烦的。

这个时候,我们借助 Source Map 功能。

如果你打开 http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js 这个文件,并且滚到页面底部,你会发现这个jquery源码的最后一行是这样的:

//@ sourceMappingURL=jquery.min.map

这便表示该压缩文件支持Source Map。并且它的同目录下,有一个 .map 后缀的文件。即 jquery.min.map。该 map文件 是一个js对象,里面保存了源码的相关信息。

简单来说,源码在打包编译时,使用打包工具或者线上工具生成一个 map 文件,这个map文件将压缩的文件与源文件建立一个映射关系。debug时,浏览器解析的是编译后,而开发者在调试时,工具面板则是源代码。

该map文件可以放在远程服务器,也可以存放本地。我们来看下如何使用 Source Map 来 debug。

如何使用

为了更加直观的对比,我们这里就拿 jquery 文件来做测试。我们在本地页面写一个 container 元素,并给它加个点击事件,然后执行它对应的 balabala 方法。

注意,要使用 sourcemap 功能,你的chrome浏览器必须开启相关设置,可通过 Setting(按F1) -> Preferences-Sources -> 勾选Enable JavaScript source maps -> 勾选Enable CSS source maps

如果开启了这些选项,则 map 文件会随页面一起下载。接着说下面两种情况:

没有使用 source Map 文件的情况:

此情况下,是引用本地的一个压缩混淆的 jquery 文件,版本是 1.8.0,文件内容的底部没有 //@ sourceMappingURL=jquery.min.map,即它本地或者其他地方没有对应的 map 文件。

<div class="container">xxx</div>

<script src="jquery-1.8.0.min.js"></script>

<script>
$('.container').on('click', function() {
$(this).balabala();
});
</script>

当我们点击 container 元素时,因为它没有对应的 balabala 方法,所以报错了。控制台是这样的:

当我们点击 jquery 对应的报错方法,会跳到 source 界面:

嗯,代码是压缩的。我们可以尝试用chrome自带的格式化工具 {},之后是这样的:

可以看到代码还是混淆的状态,根本无法与源文件对应上。

于是,我们接着尝试着使用提供了 source map 的远程 http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js

使用了 source Map 文件的情况:

同样的,在保证其他代码不变的情况下,我们将本地的 jquery 文件替换成远程的 1.9.0 版本。即:

<div class="container">xxx</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>

<script>
$('.container').on('click', function() {
$(this).balabala();
});
</script>

此时,点击 container 元素,仍然会出现文章第一张图的报错信息。但当我们点击报错信息时,你会惊讶的发现,sources 面板里所展示就是压缩、混淆前的源文件:

原始的代码行、列,以及原始的函数、变量名,一切都一清二楚。这样,我们就能愉快的调试了。

另外,我们还可以通过相应的 map 文件来调试 less 文件,原理与上面的类似。

如何生成 source map

既然 source map 这么好用,那么如何生成它呢?

其实,一些主流的编译工具,比如 gulp、webpack 都支持生成 sourcemap。只需要按照文档设置相关选项即可。比如,webpack 的配置选项就多达七项,你可以根据具体情况来配置。通常情况下,你只需要这样配置即可:

module.exports = {
devtool: "source-map",
...
}

生成的source map文件,可以存放在远程服务器,也可以放在本地服务器。

在之前,市面上流行两种形式的文件指定,分别是以 @#符号开头的。之后 //@ sourceURL//@ sourceMappingURL 已经被废弃了, 目前都建议使用 //# sourceURL=//# sourceMappingURL= 这种形式。即你看到的文件指定都是这种形式的:

//# sourceMappingURL=...

参考