【笔记】sourcemap解决压缩代码的噩梦
Jan 31, 2019笔记前端工具工程sourcemap解决压缩代码的噩梦
年前最后缕一缕sourcemap,虽然一直在用。
前言
现在绝大多数的前端项目,css及js都做了代码的压缩或打包处理,这样不但减小了代码体积、减少了请求数,也使得代码混淆提升了安全性。与此相伴的问题随之而来,随着项目的增大,压缩打包后的代码变得越来越难以debug。这也是sourcemap首要解决的问题。
1 概念
SourceMap 是一个存储源代码与编译代码对应位置映射的信息文件。这意味着你可以在优化压缩代码后找到转换前所对应的位置。
其兼容情况如下:
概括来说,得在Chrome及Firefox使。
2 语法
2.1 代码注释
在优化(压缩)后的文件的底部添加特殊注释,向支持sourcemap的浏览器提供源映射。
格式:1
//# sourceMappingURL=<url>
其中,<url>
指向一个source map文件的一个相对(于请求的URL)或者一个绝对的URL。
2.2 语法(请求头):
在压缩文件的响应中发送HTTP header来指定源映射。
格式:1
2SourceMap: <url>
X-SourceMap: <url> (deprecated)
如:1
SourceMap: /path/to/file.js.map
3 源映射文件
源映射文件包含一个JSON对象,其中包含有关映射本身和原始文件的信息。
如:1
2
3
4
5
6
7
8
9
10
11
12{
version: 3,
file: "script.js.map",
sources: [
"app.jsx",
"util.js",
"asset.jsx"
],
sourceRoot: "/",
names: ["React", "ReactRouterDOM", "ReactDOM", "HelloWorld"],
mappings: "yEAAAA,EAAAC,QAAAC,wBCAAF,..."
}
其中:
- version- 此属性指示文件所遵循的源映射规范的版本。
- file - 源映射文件的名称。
- sources - 原始源文件的URL数组。
- sourceRoot- (可选)sources将解析数组中所有文件的URL 。
- names - 包含源文件中所有变量和函数名称的数组。
- mappings- 包含实际代码映射的Base64 VLQ字符串。
映射关系正确的话,可在Chrome见源码:
- 开发者模式下设置里找到 Sources 栏,勾选上允许 JS SourceMap 与 css SourceMap (默认应该是选上的)
然后就可以尽情得开始debug调试和查看源码逻辑了。
3.1 mappings属性
sourcemap的核心对应关系在mapping属性里,它是一个Base64 VLQ字符串,可以分成三层。
- 第一层是行对应,以分号(;)表示,每个分号对应转换后源码的一行。所以,第一个分号前的内容,就对应源码的第一行,以此类推。
- 第二层是位置对应,以逗号(,)表示,每个逗号对应转换后源码的一个位置。所以,第一个逗号前的内容,就对应该行源码的第一个位置,以此类推。
- 第三层是位置转换,以VLQ编码表示,代表该位置对应的转换前的源码位置。
3.2 mappings属性位置对应的原理
如,假定mappings属性的内容如下:1
2
3
4
5{
...
mapping: "AAAAA,BBBBB;CCCCC"
...
}
表示,转换后的源码分成两行,第一行有两个位置,第二行有一个位置。
每个位置使用五位,表示五个字段。
- 第一位,表示这个位置在(转换后的代码的)的第几列。
- 第二位,表示这个位置属于sources属性中的哪一个文件。
- 第三位,表示这个位置属于转换前代码的第几行。
- 第四位,表示这个位置属于转换前代码的第几列。
- 第五位,表示这个位置属于names属性中的哪一个变量。
如果某个位置是AAAAA,由于A在VLQ编码中表示0,因此这个位置的五个位实际上都是0。
- 所有的值都是以0作为基数的;第五位不是必需的;每一位都采用VLQ编码表示;由于VLQ编码是变长的,所以每一位可以由多个字符构成。
关于VLQ在此便不过多介绍,有兴趣可点击了解wiki-VLQ
4 项目使用
4.1 webpack
1 | // webpack.config.js |
其中
- eval 每个模块都使用 eval() 执行,并且都有 //@ sourceURL。此选项会相当快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码,所以不能正确的显示行数。
- inline-source-map - SourceMap 转换为 DataUrl 后添加到 bundle 中。
- eval-source-map - 每个模块使用 eval() 执行,并且 SourceMap 转换为 DataUrl 后添加到 eval() 中。初始化 SourceMap 时比较慢,但是会在重构建时提供很快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。
- cheap-module-eval-source-map - 就像 eval-source-map,每个模块使用 eval() 执行,并且 SourceMap 转换为 DataUrl 后添加到 eval() 中。”低开销”是因为它没有生成列映射(column map),只是映射行数。
- source-map - 生成完整的 SourceMap,输出为独立文件。由于在 bundle 中添加了引用注释,所以开发工具知道在哪里去找到 SourceMap。
- hidden-source-map - 和 source-map 相同,但是没有在 bundle 中添加引用注释。如果你只想要 SourceMap 映射错误报告中的错误堆栈跟踪信息,但不希望将 SourceMap 暴露给浏览器开发工具。
- cheap-source-map - 不带列映射(column-map)的 SourceMap,忽略加载的 Source Map。
- cheap-module-source-map - 不带列映射(column-map)的 SourceMap,将加载的 Source Map 简化为每行单独映射。
- nosources-source-map - 创建一个没有 sourcesContent 的 SourceMap。它可以用来映射客户端(译者注:指浏览器)上的堆栈跟踪,而不会暴露所有的源码。
4.2 gulp
1 | const sourcemaps = require('gulp-sourcemaps'); |
5 生成工具
参考:
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com