likes
comments
collection
share

【学习图片】13.自动压缩和编码

作者站长头像
站长
· 阅读数 11

本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一时间和你分享前端行业趋势,学习途径等等。 更多开源作品请看 GitHub github.com/qq449245884… ,包含一线大厂面试完整考点、资料以及我的系列文章。

本课程中的所有语法——从图像数据的编码到支持响应式图像的信息密集标记语言——都是机器与机器之间通信的方法。

客户端浏览器与服务器相互通信有许多方式。响应式图像标记语言(尤其是srcsetsizes)使用较少的字符描述了大量信息。

不管是好是坏,这种简洁是设计出来的:让这些语法不那么简练,从而让开发者更容易理解,可能会让浏览器更难解析它们。字符串的复杂性越高,解析器出错的可能性就越大,或者在不同的浏览器之间出现无意的行为差异。

【学习图片】13.自动压缩和编码

然而,能使这些主题感到如此可怕的特性也能为你提供解决方案:机器容易阅读的语法更容易被它们编写。作为Web用户,我们肯定遇到过许多自动化图像编码和压缩的示例:通过社交媒体平台、内容管理系统(CMS)甚至电子邮件客户端上传到Web的任何图像几乎都会通过一个系统来调整大小、重新编码和压缩。

同样地,无论是通过插件、外部库、独立构建过程工具还是负责使用客户端脚本,响应式图像标记语言都很容易适应自动化。

这是围绕自动化图像性能的两个主要问题:管理图像的创建--它们的编码、压缩和你用来填充srcset属性的备用来源--以及生成我们面向用户的标记。在本模块中,你将了解一些管理图像的常用方法,作为现代工作流程的一部分,无论是作为开发过程中的一个自动化阶段,还是通过为你的网站提供动力的框架或内容管理系统,或者通过专门的内容交付网络几乎完全抽象化。

自动化压缩和编码

我们不太可能花时间手动确定每个单独图片的最佳编码和压缩级别的位置,也不会想这么做。尽管保持图像传输尺寸尽可能小很重要,但为每个图片微调压缩设置并重新保存备选源对于我们日常工作中会增加很多时间。

正如你在阅读各种图片格式和压缩类型时学到的,图像的最有效编码始终取决于其内容,正如你在响应式图片中学到的,你所需的备选尺寸将取决于这些图像在页面布局中所占据的位置。在现代工作流中,你将综合考虑这些决策,而不是单独决定——为图像确定一组合理的默认值,以最好地适应它们所用于的上下文环境。

在为一组照片图像选择编码时,AVIF在质量和传输尺寸方面是最佳选择,但其支持有限,WebP提供了一个优化的现代备选方案,而JPEG是最可靠的默认值。我们需要为在页面布局中占据侧边栏的图像制作的备选尺寸与我们在最高断点下占据整个浏览器视口的图像大不相同。压缩设置需要考虑到多个结果文件中的模糊和压缩伪影,这样就没有太多的空间来为每个图像刻意减少每个可能的字节,而需要换取更灵活和可靠的工作流程。

至于处理本身,有大量的开源图像处理库提供批量转换、修改和编辑图像的方法,它们竞争速度、效率和可靠性。这些处理库允许你一次性对整个目录中的图像应用编码和压缩设置,而无需打开图像编辑软件,并以一种方式保留原始图像源,以便在需要时调整这些设置。它们旨在运行在各种上下文环境中,从本地开发环境到 Web 服务器本身,例如,针对压缩的 Node.js 的 ImageMin 可以通过一系列插件扩展以适应特定应用程序,而跨平台的 ImageMagick 和基于 Node.js 的 Sharp 从一开始就带有惊人的功能。

这些图像处理库使开发者有可能建立专门用于无缝优化图像的工具,作为你的标准开发流程的一部分--确保你的项目将始终以尽可能少的开销引用生产就绪的图像源。

本地开发工具和工作流程

像Grunt、Gulp或Webpack这样的任务运行器和捆绑器可以用来优化图像资产和其他常见的性能相关的任务,如CSS和JavaScript的最小化。为了说明这一点,让我们来看看一个相对简单的用例:你的项目中的一个目录包含了十几张摄影图片,打算用在一个面向公众的网站上。

首先,你需要确保这些图片的编码一致、高效。正如你在前面的模块中所学到的,WebP在质量和文件大小方面都是摄影图片的有效默认值。WebP得到了很好的支持,但不是普遍支持的,所以你也要包括一个渐进式JPEG形式的回退。然后,为了利用srcset属性来有效地交付这些资产,你需要为每种编码制作多种备用尺寸。

如果使用图像编辑软件完成此操作,这将是一项重复且耗时的琐事,但是像Gulp这样的任务运行器就是为自动化这种重复性工作而设计的。gulp-responsive插件(利用Sharp)是众多选项之一,它们都遵循类似的模式:收集源目录中的所有文件,重新编码它们,并根据你在图像格式和压缩中了解到的相同标准化的“质量”简写进行压缩。然后将结果文件输出到我们定义的路径中,准备在面向用户的img元素的src属性中引用,同时保留原始文件。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

有了这样一个过程,如果项目中有人不小心将一张编码为大量真彩色PNG的照片添加到包含你的原始图像源的目录中,就不会对生产环境造成任何伤害--无论原始图像的编码如何,这项任务将产生一个高效的WebP和可靠的渐进式JPEG回退,而且压缩级别可以很容易地进行即时调整。当然,这个过程也确保你的原始图像文件将被保留在项目的开发环境中,这意味着这些设置可以在任何时候调整,只有自动输出被覆盖。

为了输出多个文件,你要传递多个配置对象--除了增加一个宽度和一个像素值,其他都是一样的。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
	.pipe(respimg({
  	'*': [{
          	width: 1000,
          	format: ['jpeg', 'webp'],
          	progressive: true,
          	rename: { suffix: '-1000' }
        	},
        	{
          	width: 800,
          	format: ['jpeg', 'webp'],
          	progressive: true,
          	rename: { suffix: '-800' }
        	},
        	{
          	width: 400,
          	format: ['jpeg', 'webp'],
          	progressive: true,
          	rename: { suffix: '-400' },
      	}]
    	})
	)
	.pipe(dest('./img/'));
}

在上面的例子中,原始图像(monarch.png)超过了3.3MB。这个任务产生的最大文件(monarch-1000.jpeg)约为150KB。最小的,monarch-400.web,只有32KB。

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

当然,你要仔细检查结果,看是否有明显的压缩伪影,或者可能增加压缩量以节省更多的费用。由于这项任务是非破坏性的,这些设置可以很容易地被改变。

而且具有韧性的过程,这个工具可以将您对高性能图像资源的知识无缝地应用于整个项目,无需任何手动干预。

响应式图像实践

填充srcset属性通常是一个简单的手动过程,因为该属性实际上只捕捉在生成源时已经完成的配置信息。在上述任务中,我们已经确定了文件名和宽度信息,这些信息将用于属性:

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

请记住,srcset属性的内容是描述性的,而不是规定性的。只要每个源的宽高比一致,过载srcset属性并不会造成任何伤害。一个srcset属性可以包含服务器生成的每个备用图像的URI和宽度,而不会引起任何不必要的请求。我们提供给渲染图像更多的备选源,浏览器就能更有效地优化请求。

正如在响应式图像中所学到的,我们将需要使用<picture>元素来无缝地处理WebP或JPEG回退模式。在这种情况下,将与srcset一起使用type属性。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

正如你所学到的,支持WebP的浏览器将识别type属性的内容,并选择该<source>元素的srcset属性作为图像候选列表。不支持image/webp作为有效媒体类型的浏览器将忽略这个<source>,并使用内部<img>元素的srcset属性。

在浏览器支持方面还有一点需要考虑:不支持任何响应式图像标记的浏览器仍需要一个回退,否则在特别旧的浏览环境中可能会出现损坏的图像。因为这些浏览器都会忽略<picture><source>srcset,所以我们需要在内部<img>src属性中指定一个默认来源。

由于缩小图像在视觉上是无缝的,而JPEG编码是普遍支持的,因此选择最大的JPEG是一个明智的选择。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

sizes可能会更加难以处理。sizes是上下文相关的,我们无法在不知道图像在渲染布局中所占空间量的情况下填充该属性。为了使请求尽可能有效率,在最终用户发出请求之前,我们的标记中就需要有一个准确的sizes属性,以便在请求样式规则和页面布局之前。完全省略sizes不仅违反了HTML规范,而且会导致默认行为等效于sizes="100vw"——告诉浏览器该图像仅受到视口本身的限制,从而选择最大的候选来源。

与任何特别繁琐的Web开发任务一样,已经创建了许多工具来抽象出手写sizes属性的过程。respImageLint是一个绝对必要的代码片段,旨在审查你的sizes属性的准确性并提供改进建议。它作为书签工具运行——您、在指向包含图像元素的完全渲染页面时在浏览器中运行。在浏览器完全理解页面布局的上下文中,它还几乎可以准确地感知在每个可能的视口大小下图像在布局中所占用的空间。

【学习图片】13.自动压缩和编码

一个用于提示你的尺寸属性的工具当然是有用的,但作为一个全盘生成这些属性的工具,它的价值甚至更大。如你所知,srcsetsizes 语法旨在以一种视觉上无缝的方式优化图像资产的请求。虽然不应该在生产中使用,但在本地开发环境中处理页面布局时,默认的尺寸占位符值为100vw是完全合理的。一旦布局风格到位,运行respImageLint将为你提供量身定做的尺寸属性,你可以将其复制并粘贴到你的标记中,其详细程度远远超过手工书写。

【学习图片】13.自动压缩和编码

尽管由服务器呈现的标记发起的图像请求过于迅速,以至于 JavaScript 无法生成客户端大小属性,但如果这些请求是由客户端发起的,则不能应用同样的推理。例如,Lazysizes 项目允许您完全推迟图像请求,直到布局已经建立,允许 JavaScript 为我们生成大小值-这对您来说非常方便,并为您的用户提供了最高效的请求保证。请记住,这种方法意味着牺牲了服务器呈现的标记和浏览器内置的速度优化的可靠性,只有在页面呈现后才启动这些请求将对您的 LCP 评分产生过度负面影响。

当然,如果你已经依赖于客户端渲染框架,例如 React 或 Vue,则您已经在承担这种债务-在这些情况下,使用 Lazysizes 意味着您的大小属性可以几乎完全抽象化。更好的是:随着 lazy loaded images 上 sizes="auto" 获得共识和本机实现,Lazysizes 将有效地成为新标准化的浏览器行为的 polyfill。

原文:web.dev/learn/image…

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

交流

有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub github.com/qq449245884… 已收录,有一线大厂面试完整考点、资料以及我的系列文章。