Skip to content

Latest commit

 

History

History
111 lines (68 loc) · 9.92 KB

2018.12.md

File metadata and controls

111 lines (68 loc) · 9.92 KB

性能反模式:Base64 编码

原文地址:https://calendar.perfplanet.com/2018/performance-anti-patterns-base64-encoding/
作者:Doug Sillars

谈到搭建迅捷 web 的规则时,很多人会从 Steve Sounders 的性能规范列表开始。这些规范很赞,而且绝大部分在性能规范刚刚发布之后的许多年里依然是真理。这张列表里的第一条规范是“少发一些 HTTP 请求”。在 Steve 的站点上用来说明第一条规范的例子里,其中一个测试展示了使用 Base64 编码的 inline 图片。通过以 Base64 来编码一个文件,你可以把 asset 加到你的 HTML 或者 CSS 里面去,这样就减少了发送给服务器的请求次数(因此满足了第一条规范)。

但是,通常来说,使用 Base64 来编码文件是反模式的。

为什么这是一种反模式?我们减少了请求,不是吗?和世上大多数的事情一样,把你的内容 inline 到你的 HTML 或 CSS 里面去是有代价的:Base64 编码过后的文件一般来说会比简单下载的文件大上20%-30%,根据文件大小的不同,单这个区别就会使得加载页面产生的延迟代价过高。第二,一旦一个文件被 Base64 编码并被嵌入到 HTML 或 CSS,它们会变得渲染阻塞(render blocking),因为它们必须在 CSS 能够被渲染及显示以前被下载好。Harry Roberts 有一篇很棒的博文,他制作了一个更慢的网站,展示了如果图片只是作为一个单独的请求下载的话,Base64 编码图片是怎样同时延缓 CSS 下载和 CSS 解析的。

在调查了一些速度较慢的 HTTP 档案的站点以后,我看到了几个把 Base64 编码使用到极致的网站:

只是因为你用 base64 编码你的图片,但并不意味着你应该这么做。一个 10.5MB 的 html 文件,而且还是 gzip 压缩过的 twitter 上的 #perfmatters 话题   pic.twitter.com/39jcAW1Uqu —— Doug Sillars (@dougsillars) December 5, 2018

我很好奇这种方法到底有多普遍,以及它的使用方法是如何影响站点加载时间的。因此,我搜索了 120 万个网站中的 response body,想要找到 Base64 编码的字串 url\(data:[\s\S]+?\)

我在 383,150 个站点上找到了 415 万个 Base64 编码过的文件(搜索过的网站中的31%)。所以,大概有 1/3 的 HTTP 档案网站正在使用反模式吗?这个世界是疯了吗?我们来看看它到底有多糟糕

Mime 类型

当我开始这项研究的时候,我假设绝大部分被编码的文件会是图片,同样我也希望大部分是 SVG 图片(因为 SVG 格式是基于 XML 的,它会受益于 Gzip 压缩,而 JPG 和 PNG 已经被压缩好了,并且 Gzip 并不会很大程度上减少他们的文件体积)。用另一个正则获取 mime 类型(并且统计了其出现次数)以后证实了我的判断。

mime 次数
image/svg+xml 2,277,575
image/pnt 1,367,013
image/gif 213,858
application/x-font-woff 87,607
application/font-woff 85,227
application/x-font-ttf 41,189
image/jpeg 17,739
font/opentype 12,572
image/svg+xml 8,812
application/vnd.ms-fontobject 8,537
application/octet-stream 8,122
font/truetype 6,421
font/woff 4,887
image/jpg 2,690
font/ttf 1,664

这个结果基本上确认了我的假设,55% 的 SVG,加上 PNG 和 GIF 以后,这三种类型的文件占了 93%,让我吃惊的是剩下的 7% 几乎都是字体。

那这些文件的总开销处于一个什么水平呢?我们可以计算所有这些文件每一个的长度(尚未压缩且以字节作为单位),作出了这张百分比图:

图1

仔细看这张图,有 3 条线明显地比整张图表都要低 ———— 它们对应的是 SVG 和 PNG 文件,JPEG 文件(深蓝色的线)同样也很低。最大的文件(在所有的百分位中)是字体!

Base64 编码的 WOFF 文件大小的中值是 29.5KB,truetype 字体的中值则接近 70KB。如 Stoyan 文中(9年前)所说,truetype 字体是基于 XML 的,Gzip 压缩过后会小 50% 左右,但是 WOFF 文件在 gzip 以后体积几乎没有节省,即使 CSS 或 HTML 被压缩的情况下。

图2

为什么在你的 CSS 里有字体?

Zach Leatherman写了一系列的文章,在里面他首次主张把 WOFF 文件 inline 到 CSS 里面(但他在文章里非常清楚地表述了异步或非阻塞的 CSS)以使用 fallback 字体(备选字体:当首选字体不能正常显示文字时的备用字体方案),并且避免不可见文本的闪现(FOIT,Flash of Invisible Text)。

编辑:为了响应这篇文章,Zach 又发布了两篇有关 inline 字体内容的文章:

但是,我找到的所有涉及 inline 字体的文章,要么

  1. 忽略任何的性能问题
  2. 推荐CSS 应该是非阻塞的

在我手动检查了几个站点以后,我检查的所有 Base64 字体之前都是渲染阻塞的 ———— 但可能有些现在不是了。

即使只有 7% 的所有 Base64 编码文件是字体,但字体文件的中值比图片文件中值要大7-30倍 ———— 很大程度上加重了每个字体产生的阻塞影响。

很多页面使用不只一种字体。在87000个 Base64 嵌入字体的页面中,大约49000(56%)个页面有不只一种 Base64 编码的字体,更加重了这个问题。3852(4.4%)个站点需要请求 10 种甚至更多的 Base64 编码字体。

图片

关于为什么要把 CSS 和 HTML 在图片之前加载,有一种很好的解释 ———— 这是为了防止图片阻塞网页的加载。当你把大图插入 CSS 的时候,实际上你正在把那些图片放到了十分重要的渲染通道上,而且有可能会阻塞你的页面加载。

比如说,这个珠宝网站有一个很大的 CSS 文件,在 3G 网络它花费了 13 秒去下载: 图3

当我检查这个文件的时候,里面有很多很小的 PNG 文件,以及一个非常大的文件,644KB 的文本,作为 PNG 大小是 458KB。当你的文件被编码成 CSS 的时候,你不再有机会响应你的图片了,因此现在你强迫每个设备接收一张 2090×580 的图片,并且它正在阻塞重要的渲染通道。

恶果

当然了,实际情况很少会出现 600KB 的 Base64 图片,大多数的图片都很小。但即使你的 css/html 里的图片很小,即使你用 SVG 图片(能够被很好地压缩),这些图片还是会对你的加载时间有很大影响。

例如,我找到了一个 CSS 文件,它在网络上有 288KB,一旦被下载和解压,这会变成 1.9MB,其中 1.45MB 是 SVG 文件。每个 SVG 文件都很小,但它们总共有 1000 个以上,很容易就会累积到 MB 的级别。使用 Chrome coverage 工具,我们发现大多数(如果不是全部的话) SVG 并未被使用。

图4

Base64 的速度

在你的网站有 Base64 编码文件会有什么影响呢?如果我们比较有 Base64 文件的网站和所有网站的数据集之间的速度指数的话,我们发现额外的 10-50KB 的 Base64 内容会让速度指数中值上升 1.3s,并且如预期的一样,更多的数量会进一步减缓页面加载速度。

图5

重复的 Base64

如果一个网站在 HTML 页面上对同一个字体或图片有多次请求,第一个请求是从服务器下载的,剩余的请求使用的是本地下载好的文件,也就是说,这个文件只会被从网络上下载一次。

但是,如果你引用一个 Base64 编码文件一次以上的话,你会把整个 Base64 文件粘贴到同一个文件的多个地方。这会导致同一个文件在每个页面加载时被下载多次。这种情形出现的频率是怎样的呢?

这是某个页面,在页面中,一个相对较小的 500 字节的背景图片在 HTML 文件中被请求了 115 次(使得页面因为重复的文本而膨胀了 50KB): 图6

对于这个 HTTP 档案数据集中的 4.1M Base64 编码文件,1.1M(27%) 在一个单页面上出现了不只一次。有 121,000(大约占所有 HTTP 档案中的 10%)个以上的页面受到了同一个对象的多次 Base64 编码下载的影响。

结论

虽然很多博文把 Base64 编码描述成一种反模式,但它仍旧在互联网里被广为使用(占 HTTP 档案的 31%)。Base64 编码文件比一个自包含的文件通常会大上 20%-30%。Base64 编码的过度使用导致了减缓页面渲染时间的更大的文件,而且,10% 的 HTTP 档案站点有对同一个 Base64 编码内容的多个入口,使得文件更为臃肿并减慢了页面的加载。

如果我们回到最初为更快网站制定的规则1 ———— "发送更少的请求",一件很明确的事情是,许多站点应该检查它们的 Base64 编码字体和图片以确定有多少 Base64 内容正在阻塞 HTML 或 CSS 中的渲染通道。也许,既然我们已经有了 HTTP2 和每次网络连接时多次文件下载的能力,我们最终可以让来自 web 的反模式的 Base64 编码退出历史舞台。