前端构建:Source Maps详解

简介: 一、前言                            当使用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

(ns sample)
(defn becomeGeek [progress] 
    (.log js/console progress)
    (if (> 100 progress)
        (becomeGeek (+ 1 progress))))

  预编译后得到如下js

复制代码
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的编译器,不但提供去除空白、注释等功能,还会对代码进行语法分析并优化代码(函数内联、变量常量化、局部变量和属性名替换等)

a = new Object    => a = {}
a = new Array     => a = []
if (a) b()        => a && b()
return 2 * 3;     => return 6;
   GC提供三种调用方式,分别为网页版网络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编码字符组成,如qCAAUH。

     4.3. VLQ编码               

    VLQ编码最早用于MIDI文件,后来被多种格式采用。它的特点就是可以非常精简地表示很大的数值。

    VLQ编码是变长的。如果(整)数值在-15到+15之间(含两个端点),用一个字符表示;超出这个范围,就需要用多个字符表示。并且规定每6bit标识一个字符。

 Continuation
  |     Sign
  |     |
  V     V
  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://www.cnblogs.com/fsjohnhuang/p/4208566.html ^_^肥仔John

 

六、参考                                    

《Javascript Source Map 详解》

《Source Maps 介绍》

Source Map Revision 3 Proposal

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

分类: 前端构建
2
0
« 上一篇: 前端构建:Less入了个门
» 下一篇: ClojureScript魔法堂:搭建开发环境
posted @ 2015-01-11 00:10 ^_^肥仔John 阅读( 14597) 评论( 2) 编辑 收藏
  
#1楼 2015-01-11 07:20 要有好的心情  
好文要顶
  
#2楼 2017-03-02 16:43 Fundebug  
压缩之后的代码抛出的错误栈根本看不懂,Fundebug实时监控网站生产环境异常并报告详尽的错误信息,特别是支持用Source Map还原真正的错误栈。这样的话,开发者能够迅速定位出错的源代码。参考: https://blog.fundebug.com/2017/02/27/fundebug-support-sourcemap/
相关文章
|
14天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
|
14天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
30 0
|
14天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
14天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
14天前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
|
14天前
|
前端开发 算法 测试技术
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
本文对比测试了通义千文、文心一言、智谱和讯飞等多个国产大模型在处理基础计数问题上的表现,特别是通过链式推理(COT)提示的效果。结果显示,GPTo1-mini、文心一言3.5和讯飞4.0Ultra在首轮测试中表现优秀,而其他模型在COT提示后也能显著提升正确率,唯有讯飞4.0-Lite表现不佳。测试强调了COT在提升模型逻辑推理能力中的重要性,并指出免费版本中智谱GLM较为可靠。
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
|
2月前
|
SpringCloudAlibaba JavaScript 前端开发
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
分布式组件、nacos注册配置中心、openfegin远程调用、网关gateway、ES6脚本语言规范、vue、elementUI
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
|
3月前
|
存储 前端开发 JavaScript
前端语言串讲 | 青训营笔记
前端语言串讲 | 青训营笔记
38 0
|
5月前
|
JSON 前端开发 JavaScript
前端Ajax、Axios和Fetch的用法和区别笔记
前端Ajax、Axios和Fetch的用法和区别笔记
86 2
|
5月前
|
前端开发 JavaScript 数据库
如何实现前后端分离-----前端笔记
如何实现前后端分离-----前端笔记