Source Maps 101
本文由韩聪根据Sayanee Basu的《Source Maps 101》所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://code.tutsplus.com/tutorials/source-maps-101--net-29173,以及作者相关信息。
——作者:Sayanee Basu
——译者:韩聪
在今天的现代化工作流程中,经过编译、压缩或者其它优化过程之后,我们在开发环境下所写的代码与最终的产品代码有着很大的不同。
指出产品代码和原本代码之间确切的映射,就是source maps发挥其作用之处。在本篇入门教程中,我们将举一个简单的项目,通过在各种JavaScript编译器中运行它,找出浏览器中source maps加入为目的。
什么是 Source Maps?
Source maps提供了一种语言无关的从产品代码到我们所写的原程序代码的映射方式。
Source maps提供了一种语言无关的从产品代码到我们所写的原程序代码的映射方式。当我们最终查看代码库,为产品做生产和准备时,从某一行确切定位到其映射到的原本的代码行,将变成一个巨大的挑戦。然而,在编译期间,一个source map会存储这种信息,因此,当我们查询一行代码,它将向我们返回原文件中对应行的具体位置!这为开发者提供了一个极大的便利,同时代码也更具可读性、可调试性。
在这篇教程中,我们将使用一个非常短小精悍的JavaScript和Sass代码,在各种编译器上运行它,然后通过source maps的帮助在浏览器中审查我们的原文件。下载演示文件之后,让我们开始吧!
浏览器
请注意,写这篇文章时,Chrome (Version 23)是支持 JavaScript Source Maps的,甚至支持Sass Source Maps。不久的将来Firefox应该也会给予支持,而它目前正处于活跃的开发阶段。现在一起来看看浏览器中怎样使用source maps带来的便利!
Chrome中的Source Map
首先,我们必须获得Chrome的支持,具体步骤如下所示:
- 打开Chrome开发者工具:视图->开发者->开发者工具
- 点击右下角的“设置”图标
- 选择“一般”,然后选择"Enable source maps"
启动
如果你想完成本教程,下载演示文件然后打开"start" 目录。文件和目录结构非常基础,scripts/script.js
中是一些简单的JavaScript。你应该可以打开index.html
,并且添加CSS颜色名称或者hex值来修改背景颜色。
$ tree
.
├── index.html
├── scripts
│ ├── jquery.d.ts
│ ├── script.coffee.coffee
│ ├── script.js
│ └── script.typescript.ts
└── styles
├── style.css
└── style.sass
看看普通的JavaScript, TypeScript 或者CoffeeScript所写的简单脚本文件。使用不同的JavaScript编译器,我们将创建一个产品备用版本,同时产生对应的source maps。
在下面的章节中,我们将使用五种不同的方式,通过关联的source map,产生一个编译、压缩过的script.js
。你可以选择测试所有的选项,或者仅仅使用你熟悉的编译器。这些选项包括:
选项A:Closure Compiler
Google的Closure Compiler, 是一个用于优化JavaScript的工具。它通过分析你的代码,删除部相关的代码,然后压缩剩下的部分来实现优化。最重要的是,它也可以产生source maps。
让我们在Closure compiler 的帮助下,通过下面的步骤来创建一个script.js
的优化版本。
- 下载最新的Closure compiler
-
解压
Compiler.jar
到目录scripts
-
从命令行进入目录
scripts
,执行如下命令,于是一个优化过的,产品代码化的script.closure.js
文件产生。
java -jar compiler.jar --js script.js --js_output_file script.closure.js
-
取消选项A的注释,确保
index.html
中引用的是新创建的文件,scripts/script.closure.js
。
当我们在浏览器中打开index.html
,定位到开发者工具的Source Panel,只有优化过的版本 script.closure.js
被引用了;我们没有办法做出返回原本有合适缩进的文件的关联。
java -jar compiler.jar --js script.js --create_source_map script.closure.js.map --source_map_format=V3 --js_output_file script.closure.js
请注意Closure Compiler 使用时有两个选项,--create_source_map
和--source_map_format
,通过source map version 3 可以创建一个source map文件, script.closure.js.map
。
//@ sourceMappingURL=script.closure.js.map
现在,当我们在浏览器中审查项目时,在开发者工具中Source Panel 的"scripts" 目录里,将同时显示原文件和优化后的版本, 即script.closure.js
。浏览器使用的无疑是index.html
中所引用的优化后的文件,source maps缺允许我们创建一个指向原文件的关联。
另外,不要尝试用断点进行调试,不过,监视表达式和变量在source maps中都尚未实现。我们希望,他们将在未来可以使用!
选项B:GruntJS Task for JSMin
如果你已经使用Grunt.js来创建流程,那么用于JSMin source maps的Grunt插件将会派上用场。它不仅优化你的代码,也能创建source map!
下面的步骤将演示,如何通过Grunt JSMin 插件为script.js
创建一个优化的版本:
-
在"start"目录根部安装 Grunt.js,初始化一个grunt文件,即
grunt.js
:
$ npm install -g grunt
$ npm view grunt version
npm http GET https://registry.npmjs.org/grunt
npm http 200 https://registry.npmjs.org/grunt
0.3.17
$ grunt init:gruntfile
-
安装Grunt插件grunt-jsmin-sourcemap; 当你完成这一步之后,
callednode_modules/grunt-jsmin-sourcemap
目录将会被创建:
$ npm install grunt-jsmin-sourcemap
-
编辑新的创建好的
grunt.js
文件,使其包含jsmin-sourcemap
任务——保持事情尽可能简单。
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-jsmin-sourcemap');
grunt.initConfig({
'jsmin-sourcemap': {
all: {
src: ['scripts/script.js'],
dest: 'scripts/script.jsmin-grunt.js',
destMap: 'scripts/script.jsmin-grunt.js.map'
}
}
});
grunt.registerTask('default', 'jsmin-sourcemap');
};
-
返回命令行,运行
grunt
;执行 jsmin-sourcemap作为默认任务,在grunt.js
中被陈述为:
$ grunt
Running "jsmin-sourcemap:all" (jsmin-sourcemap) task
Done, without errors.
-
在新建的source map文件,即
script.grunt-jsmin.js.map
中,确保源是"sources":["script.js"]
-
取消
index.html
中选项B的注释,以引用script.grunt-jsmin.js
,然后在浏览器中打开文件。
通过Grunt和其插件 jsmin-sourcemap,创建过程新建了两个文件:优化过的底部包含source mapping链接地址的script文件,和一个source map。为了在浏览器中看到它们,这两个文件你都需要。
选项C:UglifyJS
UglifyJS2是另外一个JavaScript解析器、压缩器。正如以上两个工具, UglifyJS2将创建一个优化过的script文件,追加一个source map链接地址,再创建一个包含映射到原文件的source map文件。为了使用UglifyJS,进入"start" 目录,在命令行中执行下面的语句:
-
本地安装NPM模块,
uglify-js
;nocde_module/uglify-js
目录将会被创建。
$ npm install uglify-js
$ npm view uglify-js version
2.2.3
$ cd scripts/
-
在"scripts" 目录中,我们将执行命令去创建一个优化版本,和一个source文件。选项
--source-map
和--output
来命名产出文件。
uglifyjs --source-map script.uglify.js.map --output script.uglify.js script.js
-
最后,确保
index.html
中正确地链入了script.uglify.js
选项D:CoffeeScript Redux
对于之前介绍地三个选项,从原本地代码到优化过的JavaScript,我们仅仅需要一步优化。然而,对于像 CoffeeScript这样的语言,我们需要两个过程: CoffeeScript> JavaScript>优化过的JavaScript
。在这部分中,我们将探索如何使用CoffeeScript 和CoffeeScript Redux 编译器来创建优化级Source Maps。
第一步: CoffeeScript到普通的JavaScript
从命令行进入"start,"目录。通过下面的步骤,我们将从优化过的script文件映射回 CoffeeScript:
- 安装 CoffeeScript,使称为全局npm 包
-
使用下面的命令编译 CoffeeScript文件,即
script.coffee.coffee
,来创建一个普通的 JavaScript版本:
$ coffee -c scripts/script.coffee.coffee
$ git clone https://github.com/michaelficarra/CoffeeScriptRedux.git coffee-redux
$ cd coffee-redux
$ npm install
$ make -j test
$ cd ..
-
接着,我们将创建一个source map文件,即
script.coffee.js.map
,其中包含了从原JavaScript到CoffeeScript 文件的映射信息:
$ coffee-redux/bin/coffee --source-map -i scripts/script.coffee.coffee > scripts/script.coffee.js.map
-
确保产生的 JavaScript文件,即
script.coffee.js
末尾有如下行所示的source mapping链接地址:
//@ sourceMappingURL=script.coffee.js.map
-
确保source map文件,即
script.coffee.js.map
正确地引入文件如:"file":"script.coffee.coffee"
,及源文件如"sources":["script.coffee.coffee"]
第二步:普通 JavaScript到压缩后地JavaScript
最后,我们将再次使用 UglifyJS来压缩产生的JavaScript,同时创建一个source map。这一次,将产生一个source map,据此我们可以映射回原本的 CoffeeScript文件。进入 "scripts" 目录执行下面的命令:
$ cd scripts/
$ uglifyjs script.coffee.js -o script.coffee.min.js --source-map script.coffee.min.js.map --in-source-map script.coffee.js.map
最终,确保源source map文件,即 script.coffee.min.js.map
中有正确的引入文件,如:"file":"script.coffee.min.js"
,以及正确的源,如:"sources":["script.coffee.coffee"]
。
选项E:TypeScript
TypeScript,正如 CoffeeScript,也需要两步完成整个过程:TypeScript>普通 JavaScript>压缩后的 JavaScript
。因为script使用一个jQuery插件,我们需要两个 TypeScript文件:script.typescript.ts
和jquery.d.ts
。
第一步: TypeScript到普通JavaScript
从命令行进入 "scripts"目录,执行下面的命令:
$ tsc script.typescript.ts -sourcemap
上面的命令将创建一个新的JavaScript文件,即script.typescript.js
,它的底部有一行source map地址说明://@ sourceMappingURL=script.typescript.js.map
。使用这个独立的命令,也会创建出一个map文件:script.typescript.js.map
。
第二步:普通 JavaScript到压缩后的 JavaScript
正如 CoffeeScript中的例子,第二步是使用 UglifyJS。
$ uglifyjs script.typescript.js -o script.typescript.min.js --source-map script.typescript.min.js.map --in-source-map script.typescript.js.map
最后,确保index.html
中链接了正确的script文件,即 scripts/script.typescript.min.js
。然后在浏览器中打开!
SASS中的source maps
除了JavaScript,目前,Chrome也支持SASS或者SCSS的source map。对于SASS,我们来修改一些Chrome中的设置,然后通过调试参数将SASS编译成CSS:
更改任何设置之前,请注意,在开发者工具的审查元素上,我们只能看到CSS文件参考。
打开Developer Tools experiments.
打开Dev Tools > Setting > Experiments >
勾选 "Support for SASS".
在"styles"目录中,使用下面的调试参数编译SASS。它将在每个CSS规则集之前显示@media -sass-debug-info
。
$ cd styles/
$ sass --debug-info --watch style.sass:style.css
重启开发者工具,刷新页面。
现在,当审查一个元素时,我们可以看到原本的SASS文件了!
除了简单的查看SASS文件,如果你后台正在运行 LiveReload,而又修改了SASS文件,页面也将会同步更新。例如,我们在Firefox中打开一个项目,然后使用Firebug扩展插件来审查页面。
有关于Sass中的Source Maps,可以阅读《实战Sass3.3的Source Maps》一文。
一个source map中包含的信息
如果我们查看任何一个*.map
文件,它都将包含从原文件到优化后文件的映射信息。一个source map的结构通常是JSON格式的,使用 Version 3 specifications. 它通常包含一下五个属性:
- 版本:source map 的版本,通常是"3."
- 文件:优化后文件的文件名。
- 源:原文件的文件名。
- 名称:用于映射的标记。
- 映射:映射数据。
补充资源
Source maps仍然在非常积极活跃的开发之中,但是,网上已经有了一些非常不错的资源。如果你想了解更多,不妨看看如下资料。
- Introduction to JavaScript Source Maps
- The Breakpoint Episode 3: JavaScript Source Maps
- The Breakpoint Episode 2: SASS Source Maps
- Source Maps wiki
- Multi Level Source Maps
- Source Maps Version 3 proposal
结论
我希望通过以上对多种编译器的使用介绍,已经向你展示了source map的潜力。尽管功能上目前仍然有所限制,我们希望在未来有充分的调试功能,包括访问变量和表达式。
译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!
关于韩聪
中标软件开源社区部项目助理,爱生活,略迷茫,在web前端攻城狮的道路上匍匐前进。个人博客、新浪微博,追梦的旅途中,愿与你同行。
如需转载烦请注明出处:
英文原文:http://code.tutsplus.com/tutorials/source-maps-101--net-29173