内容压缩可以像切换开关一样简单,但除此之外还有很多需要考虑的因素。 我们非常清楚需要压缩 *什么*,但是配置压缩呢? 或者静态压缩与动态压缩? Brotli 又是什么呢?
到目前为止,Brotli 在很大一部分正在使用的浏览器中都得到了支持。 虽然它在许多情况下都提供了性能优势,但也有一些进进出出的问题可能会带来挑战。 在其最高的压缩设置下,Brotli 提供比 gzip 更好的压缩比率,但在此设置下的压缩速度非常慢,以至于当内容被动态压缩时,其优势就被浪费了。 在此类情况下,您真正想要的是静态压缩。 如果您不知道静态压缩和动态压缩之间的区别,以下是一个快速复习
- 动态压缩 是在运行时发生的。 用户发出请求,内容被压缩(用户等待),然后提供压缩后的内容。
- 静态压缩 是指在用户请求之前,将资产压缩到磁盘上。 当用户请求资产时,不会发生压缩。 只是简单地从磁盘上提供预压缩的资产。
动态压缩的主要问题是,服务器必须等到压缩完成后才能回复挂起的请求。 在默认压缩级别下,这没什么大不了的。 但是,如果您要提高压缩级别以尽可能地缩小资产大小,则服务器可能需要等待压缩程序完成,从而导致延迟。 即使您实现了明显更小的文件大小,动态压缩的延迟最终也可能成为性能负担。
解决此问题的答案是可以预见的,即静态压缩,这是一个对技术博主来说并不陌生的概念。 在这篇简短的文章中,您将学习如何设置您的网站以静态压缩文件以获得最佳压缩性能,并查看此强大技术的实际结果。
如何静态压缩内容?
您如何使用静态压缩取决于您使用的 Web 服务器。 正如这位博主指出的那样,Nginx 内置了 Brotli 的静态压缩功能。 如果您使用 Express,则 shrink-ray 节点模块 将通过其自己的缓存机制提供此优势。
但是,对于其他服务器(如 Apache),情况可能并不那么简单。 Apache 的非官方 mod_brotli
模块(甚至其官方的 mod_deflate
模块用于 gzip)不提供静态压缩功能。 不过,您不一定需要服务器模块来实现此目标。 您可以在磁盘上预先压缩资产,然后使用 mod_rewrite
配置服务器以从磁盘提供这些预压缩的资产。
那么您使用什么来预压缩资产呢? 您可以使用 bash 中的二进制文件手动执行此操作,但使用 gulp 自动执行此工作要方便得多。 假设您要预压缩项目中的所有 HTML、CSS、JavaScript 和 SVG 图像,并将它们输出到不同的文件夹
const brotliCompress = () => {
let src = "src/**/*.{html,js,css,svg}",
dest = "dist";
return gulp.src(src)
.pipe(brotli.compress({
extension: "br",
quality: 11
}))
.pipe(gulp.dest(dest));
};
exports.brotliCompress = brotliCompress;
然后像这样调用 brotliCompress
任务
gulp brotliCompress
这将处理由文件通配符匹配的所有资产(在 src
变量中指定),并将 Brotli 压缩版本输出到目标目录(在 dest
变量中指定)。 styles.css
将被压缩为 styles.css.br
,scripts.js
将被压缩为 scripts.js.br
,等等。 最棒的是,质量设置为 11 可以产生最佳的压缩率。 您也可以使用 gulp-gzip 生成预压缩的 gzip 资产,以提供给不支持 Brotli 的浏览器。 其语法与 gulp-brotli 非常相似,您可以使用级别设置为 9 来最大化从该压缩方法中获得的收益。
那么下一步是什么呢? 这里需要一些 Apache 配置知识。 这位博主的技术 效果非常好
# Specify Brotli-encoded assets
<Files *.js.br>
AddType "text/javascript" .br
AddEncoding br .br
</Files>
<Files *.css.br>
AddType "text/css" .br
AddEncoding br .br
</Files>
<Files *.svg.br>
AddType "image/svg+xml" .br
AddEncoding br .br
</Files>
<Files *.html.br>
AddType "text/html" .br
AddEncoding br .br
</Files>
您还可以为那些无法理解 Brotli 编码的浏览器指定 gzip 编码的版本
# Specify gzip-encoded assets
<Files *.js.gz>
AddType "text/javascript" .gz
AddEncoding gz .gz
</Files>
<Files *.css.gz>
AddType "text/css" .gz
AddEncoding gz .gz
</Files>
<Files *.svg.gz>
AddType "image/svg+xml" .gz
AddEncoding gz .gz
</Files>
<Files *.html.gz>
AddType "text/html" .gz
AddEncoding gz .gz
</Files>
从这里,您需要几个 mod_rewrite
规则来检测浏览器 Accept-Encoding
请求头中可用的编码,然后向用户提供相应编码的资产
# Turn on mod_rewrite
RewriteEngine On
# Serve pre-compressed Brotli assets
RewriteCond %{HTTP:Accept-Encoding} br
RewriteCond %{REQUEST_FILENAME}.br -f
RewriteRule ^(.*)$ $1.br [L]
# Serve pre-compressed gzip assets
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [L]
使用这些规则,浏览器将向在 Accept-Encoding
请求头中指定的浏览器提供预压缩的 Brotli 内容。 其他浏览器将获得静态压缩的 gzip 版本。 就这样。 您刚刚学习了如何在不支持它的浏览器中向用户提供静态压缩的资产。
这对性能有什么影响?
正如您可能猜到的那样,从等式中剔除动态压缩的成本会为用户带来性能优势。 为了测试这一点,我将此更改部署到客户的静态站点并运行了一些测试。 该站点总大小约为 900 KB,包含多个样式表和脚本(包括一个相当大的 CSS/JS 框架)、一些 SVG 图像和一些相当大的 HTML。 使用 sitespeed.io,我对四种场景中的每一种运行了 50 次迭代
quality
设置为11
的动态 Brotli 压缩。- 静态 Brotli 压缩(使用相同的
quality
设置)。 level
设置为9
的动态 gzip 压缩。- 静态 gzip 压缩(使用相同的
level
设置)。
对后端时间的影响非常明显
在这里,我们看到在最高压缩级别下,动态 Brotli 压缩非常慢,(这一点已经被注意到 在一些文章中)。 但是,当我们 *预* 压缩 Brotli 资产时,我们获得了尽可能小的文件大小的全部好处,但没有最高级别动态压缩带来的惩罚。 使用 gzip 的动态压缩和静态压缩之间的差异没有那么大,但仍然值得注意。 平均而言,静态压缩有助于减少后端时间。 如果有一件事我们知道,那就是:如果我们可以减少后端时间,我们就可以减少之后发生的大多数指标。 在可能的情况下减少后端时间,您将提高整体响应速度。
注意事项和结论
静态压缩是理想的,但它不适用于所有情况。 例如,动态内容呢? 由后端语言(例如 PHP 或 C#)生成的任何页面都属于此类别。 这种内容不适合静态压缩。 在这种情况下,您必须接受静态压缩不可用。 但是,您 *可以* 做的是使用合理的动态压缩配置来压缩动态内容。 使用 Brotli 或 gzip 的默认压缩级别应该会带来性能优势,而不会对网站的第一个字节时间产生不利影响。
BREACH 攻击呢? 压缩内容容易受到 HTTPS 上此类攻击的威胁,但关键在于 BREACH 攻击仅对包含个人识别信息的内容存在问题。 在这种情况下,您可能坚持不压缩 HTML 内容,但压缩不包含敏感信息的其他类型的内容。 这是一个可行的折衷方案,但还有其他缓解方法。
除了这些场景(以及可能在您的工作流程中维护压缩资产)之外,几乎没有理由不采用静态压缩。 即使您选择的 Web 服务器不支持它,它仍然可行。 立即提升您的网站并尝试一下!
是否有离线工具可以压缩到 brotli? 对于 Gzip,您有很多软件可以在桌面上进行压缩并将其上传到您的服务器。 这就是我搜索的内容。
对于静态 Web 内容压缩,您还必须在基准测试中包含 zopfli。
对于动态内容,还有其他更快的与 gzip 兼容的库。
参见:https://sites.google.com/site/powturbo/home/web-compression
峰值内存使用量,这对于动态 Web 内容可能很重要
也包含在此基准测试中