帮助您的用户 `节省数据`

Avatar of Jeremy Wagner
Jeremy Wagner

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

在 Web 性能领域需要吸收的知识的广度和深度真是令人难以置信。至少,我几乎每周都在发现新的东西。例如:Save-Data 标头,我是在 Google Developers 的一篇文章 中发现的,该文章由 Ilya Grigorik 撰写。

如果您想了解 Save-Data 的工作原理的简短版本,我乐意为您提供:如果您选择在 Chrome 的 Android 版本(或您桌面设备上的 数据节省扩展程序)中启用数据节省功能,Chrome 发送到服务器的每个请求都将包含一个值为 OnSave-Data 标头。然后,您可以根据此标头更改网站的内容传递方式,从而为用户节省数据。这是一个非常开放的机会,因此让我们探讨一些您可以根据 Save-Data 标头采取的措施,以减少网络传输的数据量!

更改图像传递策略

我不知道您是否注意到,图像通常是任何给定页面总负载量中最大的一部分。因此,使用 Save-Data 可以采取的最有效步骤可能是更改图像的传递方式。在我的博客中,我选择将高 DPI 图像的请求重写为低 DPI 图像。当我在我的网站上提供这样的图像集时,我使用 <picture> 元素来提供 WebP 图像,并使用 JPEG 或 PNG 作为后备,如下所示

<picture>
  <source srcset="/img/george-and-susan-1x.webp 1x, /img/george-and-susan-2x.webp 2x">
  <source srcset="/img/george-and-susan-1x.jpg 1x, /img/george-and-susan-2x.jpg 2x">
  <img src="/img/george-and-susan-1x.jpg" alt="LET'S NOT GET CRAZY HERE" width="320" height="240">
</picture>

此解决方案由现代浏览器中内置的技术支持。<picture> 元素根据浏览器的功能提供最佳图像格式,而 srcset 将帮助浏览器确定哪种图像最适合任何给定设备的屏幕。不幸的是,<picture>srcset 都无法控制要为希望节省数据的用户提供的哪个图像源。它们也不应该这样做!这不是 srcset<picture> 的工作。

这就是 Save-Data 发挥作用的地方。当用户访问我的网站并发送 Save-Data 请求标头时,我使用 Apache 中的 mod_rewrite 来提供低 DPI 图像而不是高 DPI 图像

RewriteCond %{HTTP:Save-Data} =on [NC]
RewriteRule ^(.*)-2x.(png|jpe?g|webp)$ $1-1x.$2 [L]

如果您不熟悉 mod_rewrite,第一行是一个条件,它检查 Save-Data 标头是否存在以及是否包含值为 on[NC] 标志仅仅告诉 mod_rewrite 执行不区分大小写的匹配。如果满足条件,RewriteRule 将查找对以 -2x(高 DPI 版本)结尾的 PNG、JPEG 或 WebP 资产的任何请求,并将此类请求重定向到以 -1x(低 DPI 版本)结尾的资产。

现在是奇怪的部分:如果用户在启用数据节省功能的情况下访问,但随后将其关闭并在非计量(即 Wi-Fi)连接上返回,会发生什么情况?因为我们已经秘密地将对 -2x 图像的请求重写为 -1x 图像,所以浏览器将从浏览器缓存中提供低质量版本的图像,而不是从服务器请求高质量版本。在这种情况下,用户将被锁定在低质量体验中,直到他们清空浏览器缓存(或缓存条目过期)。

那么我们如何解决这个问题呢?答案在于 Vary 响应标头Vary 指示浏览器如何以及是否应该使用缓存的资产,方法是将缓存条目与特定标头(s) 对齐。Vary 的值只是其他标头名称(例如,Accept-EncodingUser-Agent 等)。如果我们希望浏览器根据 Save-Data 标头是否存在来缓存内容,我们只需将服务器配置为发送一个值为 Save-DataVary 响应标头,如下所示

<FilesMatch "\.(gif|png|jpe?g|webp)$">
  Header set Vary "Save-Data"
</FilesMatch>
<FilesMatch "\.svg$">
  Header set Vary "Accept-Encoding, Save-Data"
</FilesMatch>

在此示例中,我每次请求 GIF、PNG、JPEG 或 WebP 图像时都会发送一个值为 Save-DataVary 响应标头。对于 SVG,我发送一个值为 Accept-Encoding, Save-DataVary 标头。这样做的原因是 SVG 是可压缩的文本资产。因此,我希望确保浏览器(以及任何中间缓存,例如 CDN)在决定如何从缓存中检索条目时,除了 Save-Data 标头之外,还要考虑 Accept-Encoding 标头的值。

启用和禁用数据节省功能时的图像传递

为此,我们现在有了一种图像传递策略,可以帮助用户节省数据,并且将使用不会在用户稍后关闭数据节省功能时保留的图像填充浏览器缓存。

当然,您还可以通过其他方式在 Save-Data 存在的情况下更改图像传递方式。例如,Cory Dowdy 撰写了一篇帖子,详细介绍了如何使用 Save-Data 来提供较低质量的图像。Save-Data 为您提供了很多空间来设计最适合您的网站或应用程序的图像传递策略。

选择退出服务器推送

服务器推送 是一款很棒的功能,如果您使用的是 HTTP/2 并且可以使用它。它允许您在客户端知道需要之前就将资产发送给他们。但是,问题是,在某些情况下,它可能出乎意料地不可预测。在我的网站上,我使用它来仅推送 CSS,这通常效果很好。但是,我确实在 Apache 中调整了我的推送策略,以避免出现问题的浏览器(即 Safari),如下所示

<If "%{HTTP_USER_AGENT} =~ /^(?=.*safari)(?!.*chrome).*/i">
  Header add Link "</css/global.5aa545cb.css>; rel=preload; as=style; nopush"
</If>
<Else>
  Header add Link "</css/global.5aa545cb.css>; rel=preload; as=style"
</Else>

在这种情况下,我说“嘿,我希望您预先将我网站的 CSS 推送到用户,但前提是他们没有使用 Safari。”即使 Safari 用户访问,他们仍然会收到 preload 资源提示(尽管带有 nopush 属性以阻止我的服务器推送任何内容)。

即便如此,在为启用了数据节省功能的用户推送资产时,我们也应该格外谨慎。在我的博客中,我决定不向任何启用了数据节省功能的用户推送任何内容。为此,我对初始 <If> 标头进行了以下更改

<If "%{HTTP:Save-Data} == 'on' || %{HTTP_USER_AGENT} =~ /^(?=.*safari)(?!.*chrome).*/i">

这与我的初始配置相同,但增加了一个条件,即“嘿,如果 Save-Data 存在并设置为 on 值,请不要推送该样式表。只需 preload 它即可。”这可能不是一个很大的变化,但它是一些可以帮助访客避免浪费数据的小事情,如果推送出于任何原因无效。

更改标记传递方式

使用 Save-Data,您可以选择更改要传递的文档的哪些部分。这带来了各种机会,但在开始这项工作之前,您需要在后端语言中检查 Save-Data 标头。在 PHP 中,这样的检查可能如下所示

$saveData = (isset($_SERVER["HTTP_SAVE_DATA"]) && stristr($_SERVER["HTTP_SAVE_DATA"], "on") !== false) ? true : false;

在我的博客中,我在各种地方使用此 $saveData 布尔值来删除对给定页面内容不重要的图像的标记。例如,我的其中一篇文章 有些动画 GIF 和其他幽默图片,对于不介意的用户来说很有趣。但它们很重,并且对于传达文章的中心思想并不是真正必要的。我还删除了页眉插图和导航栏中我书籍的微型缩略图。

启用和禁用数据节省功能时的图像标记传递

从有效负载的角度来看,这肯定会有深远的影响

数据节省功能对页面有效负载的潜在影响

另一个机会是使用前面提到的 $saveData 布尔值在 <html> 元素上放置一个 save-data

<html class="<?php if($saveData === true) : echo("save-data"); endif; ?>">

使用此类,我就可以编写样式来更改 background-image 属性中使用的资产,或任何引用外部资产的 CSS 属性。例如

/* Just a regular ol' background image */
body {
  background-image: url("/images/bg.png");
}

/* A lower quality background image for users with Data Saver turned on */
.save-data body {
  background-image: url("/images/bg-lowsrc.png");
}

标记不是您可以修改传递内容的唯一内容。您可以对视频执行相同的操作,或者执行一些简单的事情,例如每页提供较少的搜索结果。完全取决于您!

结论

Save-Data 标头为您提供了一个绝佳的机会,可以帮助那些请求您帮助他们节省数据的用户。您可以使用 URL 重写来更改媒体传递方式、更改标记传递方式,或者您能想到的任何其他方式。

您将如何帮助用户 节省数据?留下评论,让您的想法被听到!


Cover of Web Performance in Action

Jeremy Wagner《Web 性能实战》 的作者,现已在 Manning 出版社出版。使用促销代码 sswagner 可节省 42% 的费用。

在 Twitter 上关注他:@malchata