摘要: 一,前言 当使用 CoffeeScript,ClojureScript 编写前端脚本时,当使用 Less,Sacc 编写样式规则时,是否觉得调试时无法准确找到源码位置呢?当使用 jquery.min.js 等经压缩后的工具库时,是否觉得连调试的门都不不知道在哪呢? 针对上述问题,google 为我们提供了 Source Maps 这一解决方案,以下内容为对 Source Maps 的学习记录,以便日后查阅.
一,前言
当使用 CoffeeScript,ClojureScript 编写前端脚本时,当使用 Less,Sacc 编写样式规则时,是否觉得调试时无法准确找到源码位置呢?当使用 jquery.min.js 等经压缩后的工具库时,是否觉得连调试的门都不不知道在哪呢?
针对上述问题,google 为我们提供了 Source Maps 这一解决方案,以下内容为对 Source Maps 的学习记录,以便日后查阅.
由于篇幅较长,特设目录一坨!
二,示例
三,Source Maps 方案详解
1. 方案结构
2. 支持的浏览器和启动方式
3. 生成器
4. map 文件详解
4.1. map 文件格式
4.2. mappings 属性
4.3. VLQ 编码
四,注意
五,总结
二,示例
首先我们使用 ClojureScript 写一段递归函数 becomeGeek
预编译后得到如下 js
(ns sample)
(defn becomeGeek [progress]
(.log js/console progress)
(if (> 100 progress)
(becomeGeek (+ 1 progress))))
sample.becomeGeek = (function becomeGeek(process) {
console.log(process);
if (((100) > process)) {
return becomeGeek.call(null, ((1) + process));
} else {
return null;
}
});
当需要调试时我们的处境就是看着 JS 代码修改 ClojureScript 代码,对于这个 becomeGeek 函数来说没多大困难,但对于整个工程来说难度不亚于看着二进制中间码来修改 Java 代码哦.下面我们通过 lein+cljsbuild 插件来生成 source maps 从而解决上述问题!
project.clj 配置信息
(defproject sample "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/clojurescript "0.0-2411"
:exclusions [org.apache.ant/ant]]
[compojure "1.1.6"]]
:plugins [[lein-cljsbuild "1.0.4"]]
:cljsbuild {
:builds [{:id "main"
:source-paths ["src-cljs"]
:compiler {:output-to "js/main.js"
:output-dir "out"
:optimizations :none
:source-map true}}]})
执行 lein 命令
$ lein cljsbulid once
然后我们开启 Chrome 的 devTools 中 js 和 CSS 的 source maps 功能即可像在 VS 上调试 C# 一样爽快了.
在 sample.cljs 文件中设置断点,然后调用 sample.becomeGeek 调试即可!
Chrome 的 devTools:FF 的 devTools:三,Source Maps 方案详解
我想大家现在已经感受到 Source Maps 的威力了,有了它我们就可以安心的使用 JS 的超集语言(ClojureScript,CoffeeScript 和 TypeScript 等),也可安心调试 jquery.min.js 等经过压缩混淆的库代码了.
1. 方案结构
Source Maps 不仅仅是一个. map 后缀的文件,而是由浏览器,.map 文件生成器和. map 文件组成的一套技术方案.
.map 文件,其实是一个关系映射文件,用于存放源码和编译后代码的文件,行号,列号和变量名的映射关系;
.map 文件生成器,每种预处理器 (Lessc,Closure,cljsc 等) 都可通过可选项设置如何生成. map 文件;
浏览器,Chrome 和 FF 均提供 Source Maps 支持(IE11 依然不支持),浏览器实质上提供的是. map 文件解析引擎,根据. map 文件内容加载源文件和在调试模式中关联源码和编译后代码.
另外编译后代码最后一行会追加一行指向. map 文件语句,指向的方式有 http uri scheme 和 data uri scheme 两种.
http uri scheme,格式为
//# sourceMappingURL=sample.js.map?rel=1420853090118
data uri scheme,就是通过对. map 文件进行 base64 编码,然后编译后代码最后一行以 data uri scheme 的形式引入. map 文件内容,格式为 //# sourceMappingURL=data:application/json;base64,Asdi.......
2. 支持的浏览器及启用方式
Chrome,devTools 的 Settings 中开启 JS 和 CSS 的 Source Maps 功能.
FF,默认已经开启 JS 和 CSS 的 Source Maps 功能.
3. 生成器
下面将介绍 Lessc,GC(Google Closure Compiler),UglifyJS,ClojureScript 和 CoffeeScript
Less 的生成器为 lessc,通过可选项 --source-map 开启生成. map 文件的功能,并通过如 --source-map-rootpath 等可选项配置. map 文件的相关信息.具体请查看《 前端构建:Less 入了个门 》
GC,作为 JS 的编译器,不但提供去除空白,注释等功能,还会对代码进行语法分析并优化代码(函数内联,变量常量化,局部变量和属性名替换等)
GC提供三种调用方式,分别为
a = new Object = >a = {}
a = new Array = >a = []
if (a) b() = >a && b() return 2 * 3; = >
return 6;
网页版
,
网络API版
和
独立应用程序
.由于GC使用Java编写,因此我们需要安装JRE.(若不想安装JRE那么可参考
@赵劼通过IKVM.NET来将clojure-compiler.jar转码为.Net版
)然后通过下面的命令生成.map文件:
$ java - jar compiler.jar--js sample.js--create_source_map. / sample - map--js_output_file sample.min.js
UglifyJS,由于 jQuery 改用 UglifyJS 作为其预编译工具令其声名远播,通过下面的命令生成. map 文件:
$ uglifyjs sample.js - o sample.min.js--source - map sample.min.map
ClojureScript,我们可以通过第二节的方式生成.map文件.
CoffeeScript,通过下面的命令生成.map文件:
coffee - c sample.min.js sample.js - m
4. map 文件详解
到这里大家已经可以得心应手地使用 Source Maps 了,接下来的内容是为想再深入理解. map 文件内容和 Source Maps 实现原理的朋友准备的.内容主要来自 @阮一峰的 《Javascript Source Map 详解》
4.1. map 文件格式
以第二节生成的. map 文件为例
{"version":3,
"file":"/C:/lein/myapp/out/sample.js",
"sources":["sample.cljs?rel=1420853090124"],
"sourceRoot":"","mappings":
";AAAA;;AAEA,oBAAA,pBAAMA,yCAAYC;AAAlB,AACC,AAAMC,YAAWD;;AACjB,GAAI,CAAA,QAAOA;AACV,OAACE,qBAAW,CAAA,MAAKF;;AADlB",
"names":["sample/becomeGeek", "process", "js/console", "becomeGeek"]}
{Number} version,Source map 的版本,目前为 3;
{String} file ,编译后的文件路径;
{Array.<String>} sources ,源码文件路径数组;
{String} sourceRoot ,源码文件的所在目录;
{Array.<String>} names ,源码中的所有变量名和属性名;
{String} mappings ,记录源码与编译后代码的位置信息.
4.2. mappings 属性
首先 mapping 属性值分为三层含义
①以分号(;)标识编译后代码的每一行,即是分号间隔的内容代表编译后代码的一行;
②以逗号(,)标识编译后代码该行中的每一个映射位置,即是逗号间隔的内容代表一个映射位置;
③以 5 组 VLQ 编码字段标识源码和编译后代码的具体映射信息.从左至右每组表示如下:
第 1 组,表示对应编译后代码的第几列;
第 2 组,表示源码所属文件在 sources 数组中的索引值;
第 3 组,表示对应源码的第几行;
第 4 组,表示对应源码的第几列;
第 5 组,表示在 names 数组中的索引值,若没有则可省略.
注意:每组 VLQ 编码字段有 0~N 个 VLQ 编码字符组成
,如
qC
A
A
U
H.
4.3. VLQ 编码VLQ 编码最早用于 MIDI 文件,后来被多种格式采用.它的特点就是可以非常精简地表示很大的数值.
VLQ 编码是变长的.如果(整)数值在 - 15 到 + 15 之间(含两个端点),用一个字符表示;超出这个范围,就需要用多个字符表示.并且规定每 6bit 标识一个字符.
Continuation
|Sign
||
VV
101011
第一位(Continuation 位)表示当前 6 个 bit 是否为当前编码段的最后一节,1 表示不是,0 表示是.
最后一位(Sign 位),当该节为当前编码段的第一节时,表示符号 1 为负号,0 为正号;若不是第一节则表示数值位.
下面对 16 进行 VLQ 编码,
1. 将 16 转换为二进制 10000;
2. 在最右边补充符号位(Sign 位)得到 100000;
3. 从最右边开始以 5bit 为一组对其进行分段,分段后不足 5bit 的在前面补 0,得到 00001,00000;
4. 倒序得到 00000,00001;
5. 为每一段添加连续位(Continuation 位)得到 100000,000001;
6. 对每段进行 Base64 编码,得到 gB.(下图为 Base64 编码字符集)
四,注意
通过 Chrome 和 FF 下 devTools 的 network 面板我们可以看到浏览器加载了. map 文件和源代码文件,现在问题来了,那么在生产环境当中用户访问网页时岂不会多加载两个开发环境使用的文件吗?
其实浏览器默认情况下(不打开 devTools 时)是不会加载. map 文件和源代码文件的,所以大家可以放心了.假如你还是怕用户误操作打开了 devTools,那么就在打包发布时不生成. map 文件就好了!
五,总结
之前尝试过 CoffeeScript,但由于编码速度虽然提高不少,但调试效率却降低更多(without source maps 之痛),导致最终回归 JS 的怀抱了.现在我们终于可以安心使用 CoffeeScript 咯!
+ 加关注
来源: http://click.aliyun.com/m/40004/