前言
如果我告诉你,位置信息就在对应的这堆字母里
成为了房间里的大象,一旦出现诸如“无法映射到源文件”“只能映射到 loader 处理后的文件”等问题,多数人是毫无头绪的;而就像 TJ 大神说的: “不要直接 copy 解决方案,要理解后自己去实现”;
-
配置项给你安排的明明白白,顺带送上生产环境、开发环境最佳实践
-
定位原理给你安排的明明白白,是怎么做到生成记录源码和处理后代码间的映射
-
感恩大奉送,编码给你安排的明明白白,base64 编码、VLQ 编码、base64-vlq 编码的三世孽缘
读至此处,您还要跑/p>
冰冰动图大合集云盘,en,是不可能给的,就给一张你们瞅瞅好了
sourcemap:devTools 配置项二三事
对于而言,我们最常见的,莫过于在 webpack 的配置项中进行使用,而有多少种供我们选择的配置呢/p>
抱歉我皮了,所谓变中取定,这么多种配置项其实只是五个关键字 eval、source-map、cheap、module 和 inline 的组合罢了,请牢记这张表,破阵心法,忽悠时方可娓娓道来。
关键字 | 含义 |
---|---|
source-map | 产生.map 文件 |
eval | 使用 eval 包裹模块代码 |
cheap | 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含 loader 的 sourcemap |
module | 包含 loader 的 sourcemap(比如 jsx to js ,babel 的 sourcemap),否则无法定义源文件 |
inline | 将.map 作为 DataURI 嵌入,不单独生成.map 文件 |
怎么理解呢战见真知。
举例详解
文件源码如下
source-map 处理后输出结果
关键字 | 特点 |
---|---|
source-map | 定位信息最全,但也.map 文件最大,效率最低 |
eval 处理后输出结果
关键字 | 特点 | 解决问题 |
---|---|---|
eval | 用“eval 包裹源代码进行执行 | 利用字符串可缓存从而提效 |
devtool: “source-map” cannot cache SourceMaps for modules and need to regenerate complete SourceMap for the chunk. It’s something for production.
devtool: “eval-source-map” is really as good as devtool: “source-map”, but can cache SourceMaps for modules. It’s much faster for rebuilds.
翻译来说划重点:加 eval 和不加是一样的,但加了后可以缓存,于是更。
处理后输出结果
关键字 | 特点 | 解决问题 |
---|---|---|
inline | 将 map 作为 DataURI 嵌入,不单独生成.map 文件 | 减少文件数 |
处理后输出结果
关键字 | 特点 | 解决问题 | 存在的问题 |
---|---|---|---|
cheap | 错误信息只会定义到行,而不会定义到列 | 精准度降低换取文件内容的缩小 |
对于而言,只会定义到出错的这一行
而对于而言,则会精准到列
存在的问题
-
错误信息只会定义到行,而不会定义到列
-
对于经由 babel 之类工具转义的代码,只能定位到转换后的代码
这就引出了我们最后的一个关键字
处理后输出结果
关键字 | 特点 | 解决问题 |
---|---|---|
module | 会保留 loader 处理前后的文件信息映射 | 解决对于使用“cheap 配置项导致无法定位到 loader 处理前的源代码问题 |
测试代码
对于而言,此时页面 debugger 展示源码是 es5 的代码,因为已经被 babal 转义了
而对于而言,则会精准到原始代码
配置项关键字小结
配置项最佳实践
开发环境
-
我们在开发环境对 sourceMap 的要求是:快(eval),信息全(module),
-
且由于此时代码未压缩,我们并不那么在意代码列信息(cheap),
所以开发环境比较推荐配置:
生产环境
-
一般情况下,我们并不希望任何人都可以在浏览器直接看到我们未编译的源码,
-
所以我们不应该直接提供 sourceMap 给浏览器。但我们又需要 sourceMap 来定位我们的错误信息,
-
一方面 webpack 会生成 sourcemap 文件以提供给错误收集工具比如 sentry,另一方面又不会为 bundle 添加引用注释,以避免浏览器使用。
这时我们可以设置
至此,关于在 webpack 中的应用层面我们就算是了解个七七八八了。但其实,这只是一个开头小菜
输出内容分析:map 文件详解
要分析实现,还是得先从现象下手,假定源文件内容为
其输出内容为
script-min.js
script-min.js.map
文件字段具体含义分析
字段 | 含义 |
---|---|
version | Source map 的版本,目前为 3 |
file | 转换后的文件名 |
sourceRoot | 转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空 |
sources | 转换前的文件,该项是一个数组,表示可能存在多个文件合并 |
names | 转换前的所有变量名和属性名 |
mappings | 记录位置信息的字符串 |
可以看到,既然我们要定位,自然最关心的是具有【记录位置信息】功能的 mapping 属性,接下来详细讲解如何分析。
mapping 属性值含义
分析角度 | 含义 |
---|---|
行对应 | 以分 (;)表示,每个分 对应转换后源码的一行。所以,第一个分 前的内容,就对应源码的第一行,以此类推。 |
位置对应 | 以逗 (,)表示,每个逗 对应转换后源码的一个位置。所以,第一个逗 前的内容,就对应该行源码的第一个位置,以此类推。 |
分词信息 | 以 VLQ 编码表示,代表记录该位置对应的转换前的源码位置、原来属于那个文件等信息。 |
-
【行对应】很好理解,即一个分 为一行,因为压缩后基本上都是一行了,所以这个没啥有用信息;
-
【位置对应】可以理解为分词,每个逗 对应转换后源码的一个位置;
-
【分词信息】是关键,如代表该位置转换前的源码位置,以编码表示;
其中【分词信息】每组最多五位(如果不是变量,只会有四位),分别是:
-
第一位,表示这个位置在【转换后代码】的第几列。
-
第二位,表示这个位置属于【sources 属性】中的哪一个文件。
-
第三位,表示这个位置属于【转换前代码】的第几行。
-
第四位,表示这个位置属于【转换前代码】的第几列。
-
第五位,表示这个位置属于【names 属性】的哪一个变量。
到此,我们也算是知道 map 文件到底是怎么组成的了。
小思考
此处附送base64vlq 在线转换地址,将上面的对应的字符串输入,将会得到对应的数字信息,如对应的是,这两者之间的映射规则就是编码。
整理目标
到此,我们整理下接下来要做的事情,抬头看天,低头走路。
我们希望解决坐标信息占用空间过大的问题,主要在于两点
-
编译后文件列 过大问题:因为会编译成一行,可以想象靠后的元素纵坐标是很大的
-
数据结构占据空间问题:数组自然比字符串更耗费空间
随着对这两个问题的思考,我们将会彻底理解,为啥我们用于记录位置信息的会是这个鬼样子
打起精神,继续学!冰冰续命
相对位置解决列 过大问题
对于第一点输出后的位置元素的列 特别大的问题,可以采用相对位置的方案进行解决,具体规则如下
-
第一次记录的输入位置和输出位置是绝对的,往后的输入位置和输出位置都是相对上一次的位置移动了多少
举例而言,假设 a.js 内容为,处理后输出 ,其 names 为:, source: [‘a.js’]
则其按照相对位置输出的关系如下
| 字符组合 | 位置类别 | 输出位置 | 输入位置 | 映射(输出 x | 属于文件在 source 的索引 | 输入 x | 输入 y | 变量在 names 的索引) | | — | — | — | — | — | | feel | 绝对 | 10, 0 | 0, 0 | 10 | 0 | 0 | 0 | 0 | | the | 相对 | -10, 0 | 5, 0 | -10 | 0 | 5 | 0 | 1 | | force | 相对 | 4, 0 | 4, 0 | 4 | 0 | 4 | 0 | 2 |
base64VLQ 解决数据结构占据空间问题
BASE64 编码
我们开发同学最初了解到大概是在小图标的处理上,当时了解到的是可以将图片的二进制转为文本,从而减少 http 请求,但只适用于小图标等体积小的内容,因为使用编码处理过会导致被处理对象体积增加 33%;那么到底是什么出现就是为了处理小图标吗解事物的经典三问
是什么/p>
在 MDN 中的定义:
是一组相似的二进制到文本(binary-to-text)的编码规则,使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。
为什么出现/p>
怎么做到的/p>
编码本身并不复杂,对使用者而言按图索骥而已,关键是它规则为什么这么设定,以及修补规则出现的原因。
既然 ASCII 码表中存在不可打印字符,那我们就定义一个新码表,其范围固定在可打印字符内。(这就意味着要多个新码表字符表示一个 ASCII 码表字符,原因很简单,你要用苹果、梨表示所有水果,那只好定义两个苹果是西瓜、两个梨是番茄、一个苹果一个梨是。。。排列组合)
新码表字符的组成单元占几个字节/p>
我们知道基础 ASCII 码,使用 7 位二进制数表示组成单元,新码表的表示范围小于 ASCII 码表(因为要确保新码表中都是可打印字符),这也就意味着,新码表使用的二进制数必须少于七位,而二进制数越少代表其能表示的字符越少,那就需要更多个二进制数来表示一个字符,而这个位数应该是越多越好的,因为这样我们所有的组成元素(新码表)就多了,于是定 6 位吧。2^6 是 64,于是新码表叫做 base64(这块纯属于个人理解,仅供推理记忆,如有错误请不吝赐教)
如何 ASCII 码进行 Base64 编解码
ASCII 码字符占 8 位二进制,而 Base64 占 6 位,取最小公倍数即为 24,即可以用 4 个 base64 字符去表示 3 个 ASCII 码字符。遂有如下转换规定
-
ASCII 码字符串根据 ASCII 码对照表转换为二进制数值;
-
把二进制数值按每 6 位进行划分;
-
-
假设字符数是 3 的倍数,比如三个 ASCII 码字符,就可以三个为一组,用四个 base64 字符来表示(,enen,应该好理解的)
-
如果待编码字符串的长度不是 3 的倍数,则用 0 补位. 如果有连续 6 位都是 0 的话, 就用来表示。
-
-
然后 6 位二进制转化为十进制根据 Base64 对照表找到编码字符.
对照表
扩展
在中,原生提供了 base64 和 ASCII 码之间的转换 API
函数名 | 功能 | 参数 |
---|---|---|
atob | base64 字符串转 ASCII 码字符串 | base64 字符串 |
btoa | ASCII 码字符串转 base64 字符串 | ASCII 码字符串 |
举例而言
以 ASCII 的 A 字符为例,A 转为二进制如上,不足三位,所以补 0,从而补齐 24 位
再 6 位为一组,对照,全为 0 的话用= 代替
所以,得出结果,A 字符对应的 Base64 编码是
至此,我们就算是对 base64 这位“最熟悉的陌生人”至少能答出个来龙去脉了
扩展-修补规则:URL 安全的 Base64 编码
修补规则出现背景
For base 64, the non-alphanumeric characters (inparticular, “/”) may be problematic in file names and URLs.
修补规则
-
不在末尾填充
-
用 替换
-
用 替换
扩展-实现:在中如何实现的编码解码/p>
编码
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!