`
yuyongkun4519
  • 浏览: 44580 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

源映射(Source Map)详解

 
阅读更多

http://www.cnblogs.com/xuld/p/5882677.html

一、什么是源映射
为了提高性能,很多站点都会先压缩 JavaScript 代码然后上线,

但如果代码运行时出现错误,浏览器只会显示在已压缩的代码中的位置,很难确定真正的源码错误位置。

这时源映射就登场了。



源映射(Source Map)是一种数据格式,它存储了源代码和生成代码之间的位置映射关系。

源映射一般使用 .map 扩展名,源映射本质是一个 JSON 文本文档,其 MIME 类型也一般设为 application/json。



二、如何使用源映射
在 JavaScript 代码中添加注释:

//# sourceMappingURL=file.js.map
浏览器(最新版 Chrome、Firefox 和 Edge 均支持)就会加载 file.js.map 并自动计算代码的实际位置。

在 Chrome 开发面板(按F12打开)的设置(按F1打开)中,可以通过勾选 "Enable Source Maps" 选项来设置是否需要加载源映射。



源映射本身并不会影响代码的执行,只会在定位错误位置时被使用。



最早浏览器是通过 "@ sourceMappingURL" 标记地址的,但这引发了一些引擎和工具的问题(和 IE 的 @cc_on 冲突),所以现在改成了 "# sourceMappingURL"。



NodeJS 中的源映射

NodeJS 在显示错误堆栈时,并不会加载源映射,可以借助 source-map-support 这个包实现。

$ npm install source-map-support
然后在代码顶部加上:

require('source-map-support/register');
这时所有堆栈位置就会被更新成真正的源码位置。

VSCode 中的源映射

VSCode 支持在调试时使用源映射,在 .vscode/launch.json 中添加:

复制代码
{
    "configurations": [
        {
            "sourceMaps": true,
            "outDir": "${workspaceRoot}/build"
        }
    ]
}
复制代码
注意必须设置 outDir,否则可能出现无法添加断点的问题。

三、如何生成源映射
现在很多生成工具都支持生成源映射,如 Uglify, Grunt, Gulp,可以参考生成工具的文档。

四、源映射格式详解
源映射本质是一个 JSON,格式如:

复制代码
{
  version: 3,
  file: 'min.js',
  names: ['bar', 'baz', 'n'],
  sourceRoot: 'http://example.com/www/js/',
  sources: ['one.js', 'two.js'],
  sourcesContent: ['', ''],
  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
}
复制代码
主要包括以下字段:

version: Source Map(源映射)的版本号,目前统一使用版本 3。
file: (可选)生成文件的路径(相对于 Source Map(源映射) 本身路径)。
names: (可选)所有名称,如变量名、函数名,下文详细介绍。
sourceRoot: (可选)所有源文件的根路径(相对于 Source Map(源映射) 本身路径)
sources: 所有源文件的路径(相对于 sourceRoot)
sourcesContent: (可选)所有源文件的内容。
mappings: 所有映射点,下文详细介绍。
其中,所有相对路径的计算方式和网页中的相对地址相同。

所有地址可以是 http:// 开头的网址或者是本地文件地址。

sources

sources 是一个数组,这意味着一个文件可以从多个文件生成过来。

很多读者会觉得这里出现的路径太多,帮大家捋一捋:

假如源文件是 xld.js ,通过压缩生成了 xld.min.js 和 xld.min.js.map ,那么:

在 xld.min.js 中需要通过 // #sourceMappingURL=xld.min.js.map 指定它的源映射。

在 xld.min.js.map 中需要通过 file: xld.min.js 指定它生效的文件。file 并不是必须的字段,该字段只用于检验。

在 xld.min.js.map 中需要通过 sources: ["xld.js"] 指定真正的源文件地址。

sourceRoot

如果 sources 有很多且有相同的前缀,则可以统一提取到 sourceRoot 中。所以以下是等价的:

{
  sources: ["a/foo.js", "a/bar.js"],
}
{
  sourceRoot : "a",
  sources: ["foo.js", "bar.js"],
}
mappings

mappings 是记录映射关系的核心。

从表面看,mappings 是一个字符串,里面由很多看似乱码的字符组成。

其实 mappings 是一个数组通过一定的方式编码得到的,这个数组包含了生成的文件中每行的映射点列表:



mappings = [
   第 1 行的映射点列表,
   第 2 行的映射点列表,
   ...
]


每行的映射点列表又是一个数组,包含了该行中所有列的映射点。





mappings = [
    [ 第 1 行第 1 个映射点, 第 1 行第 2 个映射点, ... ] // 第 1 行的映射点列表
    [ 第 2 行第 1 个映射点, 第 2 行第 2 个映射点, ... ] // 第 2 行的映射点列表
    ...
]


每个映射点又是一个数组,数组中包含了 5 个数字:

[ 生成文件的列, 源文件索引, 源文件行号, 源文件列号, 名称索引 ]
其中,名称索引可省略。源文件索引, 源文件行号, 源文件列号也可同时省略,

这表示映射点的数组长度可能是 1、4 或 5。

源映射所有行列号都是从 0 开始计数的,本文中所使用的行列号也都是从 0 开始计数的。

举个例子,比如现在有一个源映射如下:

复制代码
1 {
2   version: 3,
3   file: 'min.js',
4   names: ['bar', 'baz', 'n'],
5   sourceRoot: 'http://example.com/www/js/',
6   sources: ['one.js', 'two.js'],
7   sourcesContent: ['', ''],
8   mappings: [
9     [],
10     [],
11     [
12       [1, 0, 2, 5, 1],
13       [4, 0, 3, 6, 0]   // #13 行
14     ]
15   ]
16 }
复制代码
以 #13 行数据为例:#13 行出现在 mappings[2] 里面,因此它表示生成的文件第 2 行的信息。

#13 行包含了 5 个数字,分别表示生成文件的列 = 4, 源文件索引 = 0, 源文件行号 = 3, 源文件列号 = 6, 名称索引 = 0。

最终得到:生成的文件(即 min.js)中,行 2 列 4 的位置是从第 0 个源码(即 http://example.com/www/js/one.js)中行 3 列 6 的位置生成的,源码中相关的名称是 0(即 bar)。



通过多个映射点,可以一一定义生成的文件中每个位置对应的实际源码位置。

注意即使指定了某一行列的源码位置,也无法推断相邻行列的源码的位置,必须一一添加映射。



名称索引可以用于快速定位变量和函数压缩前的名字。

mappings 编码

为了节约存储空间,mappings 会被编码成一个字符串。

第一步:计算相对值

将映射点中每个数字替换成当前映射点和上一个映射点相应位置的差,如:

复制代码
mappings: [
    [
      [1, 0, 2, 5, 1],
      [2, 0, 3, 6, 0]
    ],
    [
      [5, 0, 2, 3, 0]
    ]
]
复制代码
其中第一个映射点不变,以后每个映射点上每个数字都减去上一个映射点(允许跨行)对应位置的数字(如果映射点元素个数不足 5,则省略部分按 0 处理),最后得到:

复制代码
mappings: [
    [
      [1, 0, 2, 5, 1],   // 不变
      [1, 0, 1, 1, -1]    // 1 = 2 - 1, 0 = 0 - 0, 1 = 3 - 2, 1 = 6 - 5, 1 = 0 - 1
    ],
    [
      [3, 0, -1, -3, 0]  // 3 = 5 - 2,  0 = 0 - 0, -1 = 2 - 3, -3 = 3 - 6, 0 = 0 - 0
    ]
]
复制代码
第二步:合并数字

将 mappings 中出现的所有数字写成一行,不同映射点使用,(逗号)隔开,不同的行使用;(分号)隔开。

1 0 2 5 1 , 1 0 1 1 1 ; 3, 0 , -1, -3, 0
第三步:编码数字

对于每个数字,都使用 VLQ 编码 将其转为字母,具体转换方式为:

1. 如果数字是负数,则取其相反数。

2. 将数字转为等效的二进制。并在末尾补符号位,如果数字是负数则补 1 否则补 0。

3. 从右往左分割二进制,一次取 5 位,不足的补 0。

4. 将分好的二进制进行倒序。

5. 每段二进制前面补 1,最后一段二进制补 0。这样每段二进制就是 6 位,其值范围是 0 到 64(含0,不含64)。

6. 根据 Base64 编码表将每段二进制转为字母:



以 170 为例,

1)转为二进制即:10101010

2)170 是正数,右边补 0:101010100

3)从右往左分割二进制:10100,  1010。

4)不足 5 位的补 0:01010,  10100

5)倒序:10100, 01010

6)除最后一个前面补 0,其它每段前面补 1:110100, 001010

7)转为十进制:52, 10。

8)查表得到:0K



任意一个整数都能通过 VLQ 编码得到一串字母和数字表示的文本。

VLQ 编码最早用于MIDI文件,它可以非常精简地表示很大的数值。

第四步:合并结果

将第二步中的每个数字进行 VLQ 编码再拼接就是最终的结果。

CAEKC,CACCC;GADHA
五、源映射相关的工具和框架
为更好理解源映射,可以使用 源映射可视化 工具。

为了处理源映射,可以使用官方的 source-map 库。

同时推荐更好用的库:source-map-builder,它相比官方的库性能更高、具有更智能的推导功能。

六、参考链接
Source Map Revision 3 Proposal

http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/

http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html
分享到:
评论

相关推荐

    十七道海量数据处理面试题与Bit-map详解

    ### 十七道海量数据处理面试题与Bit-map详解 #### 第一部分:十五道海量数据处理面试题 **题目一**:给定a、b两个文件,各存放50亿个URL,每个URL各占64字节,内存限制是4GB,让你找出a、b文件共同的URL。 - **...

    webpack将js打包后的map文件详解

    sourceMap`,可以生成CSS的Source Map文件,这样在调试CSS时也能获得源文件级别的调试体验。 ### Source Map的工作原理 Source Map文件是一个包含映射信息的JSON对象,它描述了打包文件中的代码与源代码之间的映射...

    jquery-2.0.3.min.map

    《jQuery源码映射文件详解——以jquery-2.0.3.min.map为例》 在JavaScript的世界里,jQuery是一款广泛使用的库,它极大地简化了DOM操作、事件处理、动画效果等任务。当我们处理压缩后的jQuery文件,如"jquery-2.0.3...

    前端vue+vscode断点调试详解

    前端vue+vscode断点调试详解 chrome,f12,Sources-Filesystem添加文件夹映射,即映射到本地项目的源码中进行断点调试 SourceMap能够让压缩翻译过的最终版的各种文件与原来未压缩过的文件对应起来

    VS MAP 文件使用

    ### VS MAP 文件使用详解 #### 一、引言 在软件开发过程中,程序崩溃是常见的问题之一,尤其在复杂的多线程环境中,定位崩溃的原因往往非常困难。Microsoft Visual Studio (以下简称VS) 提供了多种工具和技术来...

    webpack之devtool详解

    5. inline-source-map:将源映射信息作为 DataURL 的形式内嵌进了 bundle 中 6. eval-source-map:与 eval 类似,但是把注释里的源映射都转为了 DataURL 7. cheap-source-map:与 source-map 生成结果差不多,但生成...

    Dozer详解

    **Dozer详解** Dozer是一款强大的Java到Java对象映射库,它可以帮助开发者在不同的对象模型之间进行数据转换。在处理复杂的数据结构时,Dozer提供了便捷的方式,减少了手动转换代码的工作量,使得代码更加简洁、可...

    sourceinsight3.50.0064绿化版(中英文)

    9. `initapi.map`:初始化API映射文件,用于管理和配置SourceInsight的外部接口,允许与其他软件或系统进行交互。 SourceInsight的强大之处在于它的代码智能分析和导航功能,它能够实时分析源代码,提供快速跳转、...

    F5 VS映射.docx

    【F5 VS映射配置详解】 在IT领域,F5 BIG-IP系统是业界领先的负载均衡解决方案,它能够有效地管理和分配网络流量,确保服务的高可用性和性能。本篇文章将详细解析F5虚拟服务器(VS)配置及其相关设置,特别关注7层...

    TS打包解包代码

    例如,Webpack在生成bundle时可以生成source map文件,通过设置devtool属性(如'sourcemap'或'eval-source-map'),可以在不改变打包文件的情况下,将错误定位到原始的TS文件中。 在实际操作中,打包流程一般包括...

    Nvidia Normal Map 插件参数之详解.pdf

    - **Du/Dv**:生成环境映射凹凸贴图,适用于特定显卡技术,但在现代游戏中较少使用。 - **Q8W8V8U8**:输出Q8W8V8U8格式的凹凸贴图,包含四个8位通道。 9. **3D Preview Options** 区域:提供3D预览面板的显示...

    Nvidia Normal Map 插件参数之详解.docx

    8. **Scale**:此选项用于生成环境映射凹凸贴图,利用UV坐标的变化模拟凹凸效果,不过这种技术并不常见。 9. **Q8W8V8U8**:选择此选项将生成四通道的Q8W8V8U8格式的凹凸贴图。 10. **3D Preview Options**: - *...

    详解webpack打包后如何调试的方法步骤

    webpack.config.js 在配置devtool时,webpack给我们提供了四种选项 source-map:在一个单独... eval-source-map:使用eval打包源文件模块,在同一个文件中生产干净的完整版的sourcemap,但是对打包后输出的JS文件的执行

    MappingFramework开发手册

    - **5.2.1 <mb-mapping> 元素**:这是描述 Map 与 MessageBean 一对映射关系的根节点,包含 source-type(源数据类型)和 target-type(目标数据类型)属性,其中target-class(目标数据类全名)在特定情况下是必需...

    基于注解的Spring多数据源配置和使用

    ### 基于注解的Spring多数据源配置与使用详解 #### 一、引言 在企业级应用开发中,经常会遇到需要从多个数据库拉取数据的情况,比如进行跨库查询、定时任务中的数据分析及报表统计等。Spring框架作为Java开发领域的...

    echartMap.rar

    **ECharts 流向地图开发详解** ECharts 是一个基于 JavaScript 的开源可视化库,由百度公司开发,广泛应用于数据可视化的各种场景。本教程将深入探讨如何使用 ECharts 开发流向地图,尤其在不依赖在线地图服务的...

    Dozer 使用

    在这个例子中,`SourceObject` 和 `TargetObject` 分别是源对象和目标对象,`mapper.map()` 方法用于执行映射。 **三、映射配置** Dozer 允许通过 XML 配置文件进行更精细的控制。例如,你可以指定特定字段的映射...

    webpack高级配置与优化详解

    3. **eval-source-map**: 结合了eval和source-map的优点,提供每模块的eval执行,并生成DataUrl形式的SourceMap。虽然打包速度较慢,但能够提供更为准确的调试信息。 4. **cheap-source-map**: 不包含列信息,也不会...

Global site tag (gtag.js) - Google Analytics