渐进式图像的并行流


渐进式图像渲染和HTTP/2多路复用技术已经存在了一段时间,但现在我们以一种新的方式将它们组合在一起,使它们更加强大。使用渐进式流式传输图像可以在一半的时间内加载,浏览器可以更快地开始渲染页面。

在HTTP/1.1连接中,服务器对资源发送到客户端的顺序没有任何选择;他们必须按照Web浏览器请求的确切顺序发送响应。HTTP/2通过添加多路复用和优先级来改进这一点,这使得服务器可以准确地确定发送的数据和时间。我们利用这些新的HTTP/2功能,通过更快地发送最重要的图像数据片段来提高逐行图像加载的感知速度。

此功能与所有主流浏览器兼容,并且不需要对页面标记进行任何更改,因此很容易采用!

什么是渐进式图像渲染?

基本图像严格从上到下加载。如果浏览器仅收到一半图像文件,则它只能显示图像的上半部分。渐进式图像的内容不是从上到下排列,而是从低像素到高像素。接收一小部分图像数据允许浏览器显示整个图像,但保真度较低。随着更多数据的到来,图像变得更清晰,更清晰。

图1

这在JPEG格式中效果很好,其中仅需要大约10-15%的数据来显示图像的预览,并且在50%的数据处,图像看起来几乎与整个文件的传送时一样好。渐进式JPEG图像包含与基线图像完全相同的数据,只是以更有用的顺序重新洗牌,因此渐进式渲染不会增加文件大小的任何成本。这是可能的,因为JPEG不会将图像存储为像素。相反,它将图像表示为频率系数,其类似于可以以任何顺序混合在一起的一组预定义模式,以重建原始图像。JPEG的内部工作原理非常吸引人,您可以从我最近的内容中了解更多相关信息performance.now()

最终的结果是,图像在一半的时间内看起来几乎可以完全加载。页面看起来在视觉上完整,可以更快地使用。其余的图像数据在不久之后到达,将图像升级到完整质量,然后访问者有时间注意到任何遗漏。

HTTP/2渐进式流式传输

但是有一个问题。网站有多个图像(有时甚至是数百个图像)。当服务器一个接一个地发送图像文件时,渐进式渲染无助于此,因为整体上图像仍然按顺序加载:

图2

拥有一半图像的完整数据(而另一半没有数据)看起来不如拥有所有图像的一半数据。

还有另一个问题:当浏览器还不知道图像大小时,它会将页面放置在占位符中,并在每个图像加载时中继页面。这可以使页面在加载期间跳跃,这对于用户来说是不优雅的,分散注意力并且令人讨厌。

我们新的渐进式流媒体功能极大地改善了这种情况:我们可以同时发送所有图像,并行发送。这样浏览器可以尽快获取所有图像的大小信息,可以绘制所有图像的预览而无需等待大量数据,大图像不会延迟加载样式,脚本和其他更重要的图像资源。

这种并行流式传输图像流的想法与HTTP/2本身一样古老,但它需要在Web服务器的低级部分进行特殊处理,到目前为止还没有大规模实现。

当我们改进HTTP/2优先级时,我们意识到它也可以用来实现这个功能。整个图像文件既不高也不低优先。优先级在每个文件中发生变化,动态重新优先级化为我们提供了我们想要的行为:

  • 他的图像标题包含图像大小是非常高的优先级,因为浏览器需要尽快知道大小的页面布局。图像标题很小,因此将其发送到其他数据之前并没有什么坏处。

图3

  • 显示图像预览所需的图像中的最小数据量具有中等优先级(我们希望尽快为已卸载的图像插入“对应位置”,但也为脚本,字体和其他资源)

图4

  • 其余的图像数据是低优先级的。由于页面已经完全可用,因此浏览器可以在最后一次流式传输以优化图像质量。

知道每个阶段要发送的确切数据量需要了解图像文件的结构,但是让我们的Web服务器解析图像响应并在协议级别硬编码格式特定的行为似乎很奇怪。通过将问题框定为优先级的动态变化,能够优雅地将低级网络代码与图像格式知识分开。我们可以使用Workers或离线图像处理工具来分析图像,并指示我们的服务器相应地更改HTTP/2优先级。

关于图像并行流的好处是它不会增加任何开销。我们仍然发送相同的数据,相同数量的数据,我们只是以更智能的顺序发送它。此技术利用现有的Web标准,因此它与所有浏览器兼容。

瀑布流

以下是来自WebPageTest的瀑布图,显示了常规HTTP/2响应和渐进式流式传输的比较。在这两种情况下,文件完全相同,传输的数据量相同,并且整个页面加载时间相同(within measurement noise)。在图表中,蓝色部分显示数据传输时间,绿色显示每个请求空闲时。

图5

第一个图表显示了典型的服务器行为,使图像大部分按顺序加载。图表本身看起来很整洁,但加载该页面的实际体验并不是很好 - 最后一张图片直到几乎结束才开始加载。

第二个图表显示并行加载的图像。整个图表中的蓝色垂直条纹是早期发送的图像标题,然后是几个渐进渲染阶段。您可以看到所有图像的有用数据更快到达。您可能会注意到其中一个图像已经在一个块中发送,而不是像所有其他图像一样分割。那是因为在TCP / IP连接的最开始,我们还不知道连接的真正速度,我们必须牺牲一些机会来确定优先级,以便最大化连接速度。

指标与其他解决方案相比较

还有其他技术旨在快速提供图像预览,例如低质量图像占位符(LQIP),但它们有几个缺点。它们为占位符添加了不必要的数据,并且通常会干扰浏览器的预加载扫描程序,并且由于依赖于将预览升级为完整图像所需的JavaScript而延迟加载全部质量的图像。

  • 我们的解决方案不会导致任何其他请求,也不会添加任何额外数据。总页面加载时间不会延迟。
  • 我们的解决方案不需要任何JavaScript。它利用了浏览器中原生支持的功能。
  • 我们的解决方案不需要对页面标记进行任何更改,因此在站点范围内部署非常安全且容易。

用户体验的改善反映在性能指标上,例如SpeedIndex指标和视觉完成时间。请注意,对于常规图像加载,视觉进度是线性的,但是使用渐进式流式传输时,它会快速跳转到大部分完成:

图6

图7

充分利用渐进式渲染

避免破坏JavaScript的效果。隐藏图像并等到onload事件以显示它们(带淡入等)的脚本将打败渐进式渲染。渐进式渲染最适合使用旧的<img>元素。

它只是JPEG吗?

我们的实现与格式无关,但渐进式流式传输仅对某些文件类型有用。例如,将它应用于脚本或样式表是没有意义的:这些资源被渲染为全有或全无。

图像标题的优先级(包含图像大小)适用于所有文件格式。

渐进式渲染的好处是JPEG(在所有浏览器中都支持)和JPEG 2000(在Safari中支持)所特有的。GIF和PNG具有隔行扫描模式,但这些模式的代价是压缩率较低。WebP甚至根本不支持渐进式渲染。这造成了两难:WebP通常比同等质量的JPEG小20%-30%,但渐进式JPEG似乎加载速度提高50%。有下一代图像格式支持渐进式渲染比JPEG更好,压缩比WebP更好,但它们在Web浏览器中尚不支持。与此同时,您可以通过更改Cloudflare仪表板中的波兰语设置,在WebP的带宽节省或渐进式JPEG的更好感知性能之间进行选择。

用于实验的自定义标题

我们还支持自定义HTTP标头,允许您试验并优化网站上其他资源的流式传输。例如,您可以让我们的服务器发送具有高优先级的动画GIF的第一帧,并优先考虑其余部分。或者,您可以在<body>加载之前优先加载HTML文档的<head>中提到的资源。

可以仅从Worker设置自定义标头。语法是以逗号分隔的文件位置列表,具有优先级和并发性。优先级和并发性与上一篇博客文章中描述的整个文件cf-priority标头相同。

cf-priority-change: :/, …

例如,对于渐进式JPEG,我们使用类似的东西(这是在Worker中使用的JS片段):

let headers = new Headers(response.headers);
headers.set("cf-priority", "30/0");
headers.set("cf-priority-change", "512:20/1, 15000:10/n");
return new Response(response.body, {headers});

它指示服务器最初使用优先级30,同时发送前512个字节。然后使用一些并发(/1)切换到优先级20,最后在发送15000字节的文件后,切换到低优先级和高并发(/n)以传递文件的其余部分。

我们将尝试拆分HTTP/2帧以匹配标头中指定的偏移量,以尽快更改发送优先级。但是,优先级并不保证不同流的数据将完全按照指示进行多路复用,因为服务器只有在具有等待同时发送的多个流的数据时才能确定优先级。如果某些响应很快从上游服务器或高速缓存到达,则服务器可以立即发送它们,而无需等待其他响应。

最后

我希望你发现这篇文章有用!你可以关注我的博客。请在下面的评论中留下任何问题。我很乐意帮忙!


文章作者: 左智文
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 左智文 !
评论
  目录