Base64 编码和性能,第 1 部分

原文链接:https://csswizardry.com/2017/02/base64-encoding-and-performance/

作者:Harry Roberts
来源:CSS Wizardry

本文一共分为两篇。阅读第 2 部分

几年前我们只是单纯的考虑,“减少请求数量”,使页面加载速度更快。虽然这是一个合理化的建议,但实际上,我们还可以通过合理的分配资源的请求数,来达到我们的目的。

Base64编码就是为了减少请求数,而出现的“最佳实践”。例如:将一张图片编译为文本资源,嵌入到样式表中。减少了请求数,同时也展示了图片资源。不可想象是么?

不。

不幸的是,Base64编码资源是一种非常反面的教材1。在这篇文章中,我会分享一些关于关键路径优化,Gzip,当然,还有对于Base64编码的看法。

让我们看一些代码

我写这篇文章的动机是因为我刚好为客户做一个性能审计,我遇到了我描述的问题。这是来自实际客户端的实际样式表:示例是匿名的,但这是一个完全真实的项目。

写这篇文章的起因是,我最近正在为客户开发一个性能检测,正好遇到了这个问题。这是一个匿名的真实案例。

我打开了一个网页,查看了网络配置, 发现了一个CSS文件(因为我们不想看到12个样式表的请求链接,所以通常会统一到一个文件中),在不压缩的情况下,竟然达到了925k。线上的文件字节数大大减少,还是接近232K大小。

样式表文件竟然这么大?我其实都不用看,也知道里面一定有一些Base64编码格式。我不是说文件大一定是因为Base64的原因(可能是因为插件,代码组织混乱,历史原因等),但文件大通常都是因为Base64。

  1. 不管是不是Base64,925K的CSS都是惊人的。
  2. 压缩后也只是减少到759K
  3. Gzipping 使得降到232K。相同的代码,减少了将近693K。
  4. 232K 对于线上文件来说,还是很大。

直勾勾的盯了屏幕88ms,只是为了解析那个大小的样式文件。
让它通过网络加载,才是我们遇到的问题。

我美化了文件2,保存到本地,执行了CSSO,然后通过 Gzip 运行它定时输出的常规设置。如下:

harryroberts in ~/Sites/<client>/review/code on (master)
» csso base64.css base64.min.css

harryroberts in ~/Sites/<client>/review/code on (master)
» gzip -k base64.min.css

harryroberts in ~/Sites/<client>/review/code on (master)
» ls -lh
total 3840
-rw-r--r--  1 harryroberts  staff   925K 10 Feb 11:23 base64.css
-rw-r--r--  1 harryroberts  staff   759K 10 Feb 11:24 base64.min.css
-rw-r--r--  1 harryroberts  staff   232K 10 Feb 11:24 base64.min.css.gz

接下来要做的是看看这些字节中有多少是来自 Base64 编码文件。为了解决这个问题,我简单地(粗略地)删除了包含data:字符串的所有行 / 声明(对于 Vim 用户来说是:g/data:/d33)。Base64 编码大部分用于图像 / 精灵和几种字体。然后我将这个文件保存为no-base64.css,并做了相同的缩小,和Gzip 压缩:

harryroberts in ~/Sites/<client>/review/code on (master)
» ls -lh
total 2648
-rw-r--r--  1 harryroberts  staff   708K 10 Feb 15:54 no-base64.css
-rw-r--r--  1 harryroberts  staff   543K 10 Feb 15:54 no-base64.min.css
-rw-r--r--  1 harryroberts  staff    68K 10 Feb 15:54 no-base64.min.css.gz

在我们未压缩的 CSS 中,消失了整整217K的 Base64。仍然是很大的 CSS量(708K 是相当不方便),但我们已经想办法摆脱了23.45%的代码。

接下来真正让我们惊讶的是,Gzip压缩后,我们的线上代码从 708K 降到了 68K! 哇,这节省了 90.39%!

Gzip 保存…

Gzip 真不可思议!这可能是最好的方式从开发者的角度来保护用户。通过在线压缩,节约了90%的CSS代码。毫不费力的从 708K 下降到 68K。

… 有时

但是,这是 Gzip 的非 Base64 编码版本。我们看看原始的 CSS(使用 Base64 编码的 CSS),我们发现我们只节省了 74.91%。

Base64毛尺寸压缩大小保存
925K232K74.91%
没有708K68K90.39%

两个文件之间的差异是大概在 164K(70.68%)。我们可以通过将这些资源移动到更合适的地方,减少 164K 的 CSS。

Base64 压缩非常不好。下次有人尝试,可以告诉他们Gzip这点(如果他们试图证明 Base64更好)。

那么为什么 Base64 这么糟?

好吧,所以我们很清楚,现在 Base64 增加文件大小的方式,Gzip 无法真正帮助我们,但这只是问题的一小部分。我们为什么这么害怕文件大小的增加?单个图像可能大小就超过 232K,所以我们是不是从那里开始研究更好?

不错的问题,我很高兴你提到了图像…

我们需要谈论图像

为了理解 Base64 是多么糟糕,我们首先需要了解好的 图像是什么。争议点:图像不像你认为的性能那么差。

确实,图像是一个问题。事实上,他们是页面膨胀的第一贡献者。截至 2016 年 12 月 2 日,图片占平均网页的 1623K(约 65.46%)。这使得232K 样式表如沧海一粟。但是,浏览器处理图片和样式表的方式有着根本区别:

图像不阻止渲染; 样式表阻止渲染。

浏览器渲染页面,不会等待图片加载完成,甚至说,图片加载失败,页面也会渲染。图片不是关键资源,即使很大,也没有关系。

另一方面,CSS 是一个关键资源。在构建渲染树之前,浏览器无法开始渲染页面。浏览器在构造 CSSOM 之前不能构造渲染树。在样式表没有解压,解析,加载完成之前,他们不能构造 CSSOM。CSS 是瓶颈。

现在你应该可以明白为什么我们这么害怕的 CSS 字节数:他们延迟了页面渲染,展示给用户一个空白网页。希望你现在可以意识到把 Base64 编码图像加到你的 CSS 文件中是多么的讽刺:你只是把数百千字节的非阻塞资源转换为阻塞资源去追求性能。原本不需要这些资源加载完毕,就可以展示页面,但现在他们被迫走上了关键资源的道路。这并不意味着图像更早到达; 而是意味着关键资源 CSS 要稍后到达。真的这么糟糕吗?

恩,是的。

浏览器是很聪明的。真的很聪明。他为我们做了很多性能优化,他们知道如何更好(多总比没有好)。下面我们看下响应式:

.masthead {
  background-image: url(masthead-small.jpg);
}

@media screen and (min-width: 45em) {

  .masthead {
    background-image: url(masthead-medium.jpg);
  }

}

@media screen and (min-width: 80em) {

  .masthead {
    background-image: url(masthead-large.jpg);
  }

}

我们给浏览器三个可能会用到的图片,但它只会下载其中一个。它需要哪一个,获取那一个,其他两个并不加载。

但一旦我们 Base64 这些图像,三张图片的字节数都会被下载,相当于我们有了三倍(或左右)的开销。这里是实际项目中用到的CSS代码块(没有使用base64,完整的代码片段Gzip之前总共 26K,之后18K):

@media only screen and (-moz-min-device-pixel-ratio: 2),
       only screen and (-o-min-device-pixel-ratio:2/1),
       only screen and (-webkit-min-device-pixel-ratio:2),
       only screen and (min-device-pixel-ratio:2),
       only screen and (min-resolution:2dppx),
       only screen and (min-resolution:192dpi) {

  .social-icons {
    background-image:url("data:image/png;base64,...");
    background-size: 165px 276px;
  }

  .sprite.weather {
    background-image: url("data:image/png;base64,...");
    background-size: 260px 28px;
  }

  .menu-icons {
    background-image: url("data:image/png;base64,...");
    background-size: 200px 276px;
  }

}

所有用户,无论是否是在视网膜屏上(甚至用户使用的是不支持媒体查询的浏览器),在浏览器器未展示页面之前,都将被迫下载额外的 18K CSS。

Base64的资源总是被下载,哪怕完全用不上。相较于浪费资源,它更大的缺点是阻塞渲染。

我们需要谈论字体

到目前为止我只提到了图像,但字体除了一些在浏览器如何处理 无样式的字体 / 看不见文本的闪烁问题(FOUT 或 FOIT)上有细微区别外,几乎完全相同。这个项目中的字体未压缩的 CSS总共 166K (124K Gzipped(依旧很大))。

如果不是一篇文章,字体也不算不上是关键资源,注意:你的页面没有字体也可以渲染。问题是,浏览器处理 web 字体各不相同:

  • Chrome 和 Firefox 最多只能显示 3 秒钟内显示的文字。如果网络字体在这三秒内到达,文本从不可见切换到您的自定义字体。如果字体在 3 秒后仍未到达,则文本使用系统默认字体。这是 FOIT。
  • IE 会立即显示系统备用字体,然后在自定义字体到达时将进行替换。这是 FOUT。在我看来,这是最优雅的解决方案。
  • Safari 会不显示字,直到字体到达。如果字体从未到达,它也没有备用字体。这是 FOIT。这绝对是可憎的。您的用户将永远无法看到您网页上的任何文字。

为了解决这个问题,人们开始用 Base64 将他们的字体内嵌到样式表:如果 CSS 和字体完全同时到达,那么就不会出现 FOIT 或 FOUT,因为 CSSOM 的渲染和字体几乎同时开始。

和上面的一样,字体移到关键路径,不会加快他们的交付,延迟了您的 CSS。有一些非常聪明的字体加载解决方案, 但 Base64 不是其中之一。

我们需要谈论缓存

Base64 还影响我们拥有更复杂的缓存策略:将我们的字体,图像和样式联系在一起,会受同样的规则约束。这意味着如果我们在 CSS 中只改变一个十六进制值 - 六个字节的新数据的更改 - 我们必须重新下载几百 KB 的样式,图像和字体。

事实上,在这里字体是罪魁祸首:字体是改动的频率极低。另一个开发者和我一起运行一个长期的项目:CSS 最后一次修改时间是昨天; 字体文件的最后一次修改时间是八个月前。想象一下,每当您更新样式表中的任何内容时,强制用户重新下载这些字体。

Base64 编码意味着我们不能根据它们的更新频率单独地缓存文件,意味着无论什么时候有更改,都要重新缓存不相干的文件。这太浪费资源了。

基本分离的关注点:字体的缓存不应该依赖于图像缓存,不应该依赖于样式的缓存。


好吧,让我们快速回顾一下:

  • Base64 编码增加了文件大小,并且无法有效压缩(例如 Gzip)。文件大小的增加会延迟渲染,发生在渲染阻塞资源。

  • Base64 编码强制非关键资源在关键路径上加载。(例如图像,字体)这意味着,在这种特殊情况下,我们不是现在 68K 的CSS 文件,而是下载了3.4倍文件大小。我们在让用户等待他们原本不需要等待的资源加载!

  • Base64 编码强制下载所有资源字节数,即使不使用。这纯属浪费资源,并且,它发生在我们的关键路径上。

  • Base64 编码限制了我们单独缓存资源; 图像和字体现在与样式的缓存规则一致,反之亦然。

总而言之,这是一个非常不好的情况:请避免 Base64。

数据会话

所有这篇文章所写的都是我目前已知的东西。我没有用任何数据去进行测试:只是展示了浏览器的工作方式。接下来,我决定去做一些测试,用事实和数据说话。转到第 2 部分阅读更多


  1. 在 Chrome 的 “ 来源 ” 标签中打开样式表,然后按下{}文件左下角的图标即可。
  2. 在 Chrome 的 “ 来源 ” 标签中打开样式表,然后按下{}文件左下角的图标即可。
  3. (:g) 表示全局命令查找包含data:(/data:)和删除(/d)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值