除ie外,大部分现代浏览器都已经支持原生的基于 Base64 的 encode 和 decode,例如 window.btoa
和 window.atob
函数。
data URI
URI 是 (uniform resource identifier) 的缩写,它定义了接受内容的协议以及附带的相关内容,如果附带的相关内容是一个地址,那么此时的 URI 也是一个 URL (uniform resource locator)
data URI 的格式:
1 | data:[<mediatype>][;base64],<data> |
在这种格式中,data:
就是 URI 的协议,表明这是一个 data URI。
以下是一个HTML代码片段:
1 | <img src="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7" alt="Larry"> |
Base64 使用的优缺点
原本页面由 HTML、CSS、和若干图片组成,如果将图片通过 data URI 的形式合并到CSS文件中,页面就可以完全由 HTML、CSS 来组成。
对于前端来说,显而易见好处是能够减少一个图片的 HTTP 请求,而缺点可能就不够显而易见。
样式表会变得很大,从而阻塞关键下载和渲染。通俗地讲,图片文件或字体文件的体积转移到了 HTML 或 CSS中,而后者的体积直接影响渲染,导致用户会长时间注视空白屏幕。HTML 和 CSS 阻塞渲染,图片不会。
使用 Gzip 压缩文本
Gzip把原文本中多次出现的相同字符串记为一个“标记”,所以文本中重复出现的字符串越多,压缩率越高。
HTML 中重复出现大量的 HTML 标签以及类名等,CSS中重复出现大量的属性,JavaScript 中重复的函数调用等(即使经过混淆)。因此 HTML、CSS、JavaScript 的 Gzip 压缩率都是很高的,最高可达到90%。
而图片经过Base64转化后变成的文本是无规律的,所以在Gzip中不能达到较高的压缩率。
Base64 缓存
Base64跟CSS混在一起,难以分别进行缓存设置和更新。
平常的项目中,CSS文件的修改频率是较高的,图片其次,而字体文件,几乎是几个月甚至一年以上才修改一次。我们一般会为不同类型的文件设置不同的缓存失效时间,以及在更新某个文件之后单独更新这个文件的时间戳。
混在一起之后,即使我们只是想更新CSS规则里面一个字号,整个几百K的文件就会重新生成。用户不得不在每次小型更新后重新下载整个大文件,这违背了基本的缓存原则。
CSSOM 渲染
Base64跟CSS混在一起,大大增加了浏览器需要解析CSS树的耗时。其实解析CSS树的过程是很快的,一般在几十微妙到几毫秒之间。如果CSS文件中混入了Base64,那么(因为文件体积的大幅增长)解析时间会增长到十倍以上。
适合使用 Base64 的场景
对于仅有一两个小图标的网页,开发者也许没有必要专门生成一个雪碧图。
如果网页还能保证几个月不会更新,那么缓存不可控的问题也不会凸显。
对于一个使用了背景平铺图片的网页,平铺图片无法合并到页面资源雪碧图中,这时使用 data URI 也许是一个合理的选择。
Base64 的实现原理
下面的步骤为 Base64 编码过程:
- Base64的编码都是按字符串长度,以每3个8bit的字符为一组,
- 针对每组,首先获取每个字符的ASCII编码,
- 将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
- 然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节(因为 0 - 63 需要6位字节表示)
- 将这4个8bit的字节转换成10进制,对照Base64编码表得到对应编码后的字符。
1 |
|
(注:1. 要求被编码字符是8bit的,所以须在ASCII编码范围内,\u0000
-\u00ff
,中文就不行。如果被编码字符长度不是3的倍数的时候,则都用0代替,对应的输出字符为=)
编码过程
字符长度为能被3整除时
字符长度为能被3整除时:比如“Tom” :
1 | T o m |
1 | btoa(‘Tom’) = VG9t |
字符串长度不能被3整除时
字符串长度不能被3整除时,比如“Lucy”:
1 | L u c y |
由于 Lucy
只有4个字母,所以按3个一组的话,第二组还有两个空位,所以需要用 0
来补齐。这里就需要注意,因为是需要补齐而出现的 0
,所以转化成十进制的时候就不能按常规用 Base64 编码表来对应,所以不是a, 可以理解成为一种特殊的“异常”,编码应该对应 =
。
实现 Base64
1 | /** |