适用于任何 Web 框架的完美图像优化
这篇文章来自我的“Web Wizardry”简报,我会在其中探索常见 Web 开发问题的常用解决方案(无论您喜欢哪个框架)。如果您喜欢,请免费注册🪄
如果你已经建站一段时间了,“优化图片”可能听起来就像“多吃蔬菜”一样。图片优化确实有利于网站的健康,能让你的 SEO 更上一层楼……但手动压缩每张图片对我来说可不是什么好事🤢
因此,我们将讨论以下轻松获胜的方法:1)使用元素优化图像文件格式和大小picture
,以及 2)使用 11ty 的自动化流程,您可以将其带到您选择的构建设置中💪
💁目标读者: 本文面向构建“模板驱动”静态网站(例如 11ty、Jekyll、Hugo、纯 HTML)或“组件驱动” Web 应用(例如 NextJS、Gatsby 等)的开发者。如果您使用 Wordpress 或 Shopify 等网站构建器,那么本文可能不适合您!
🥦 那么我的图像现在有什么问题?
为了说明其中的利害关系,以下是我最近的一篇博客文章中的灯塔评级(请注意,图片是用tinyJPG压缩的!)
哎呀!抓取这些图片竟然要 10 秒?Chromium 在“较慢”的网速下确实做了一些限制,但显然这些 KB 评分相当高(尤其是对于移动用户而言)。
这只是为了说明图像优化不仅仅是压缩!还有:
- 提供正确的格式,最好是 JPG,
.webp
或者.avi
特别如此 - 提供合适的尺寸,最好是同一张图片的多个副本,宽度和高度不同
- 在正确的时间加载,尽可能使用“延迟”加载
- 哎呀,即使包含
alt
文本也会从可访问性和 SEO 的角度影响您的网站!
我学到了一些使用picture
元素解决格式和尺寸问题的方法,我的灯塔肯定因此感谢我😄
picture
🌅 修复元素的格式 + 大小问题
那么,我们如何才能为合适的用户提供不同的图片文件呢?好吧,让我们从一个简单的图片元素开始,像这样:
<img src="/assets/mega-chonker.jpg" width="1000" height="600" alt="A perfectly sized cat" />
请参阅此便捷图表来了解“chonk”级别
现在,假设我们打开了图片编辑器,并为移动端用户保存了一个较小的版本,比如说 600 像素宽。你可以设置一些 CSS 来实现根据屏幕宽度热切换图片的功能:
<img class="desktop" src="/assets/chonker-1000w.jpg"
width="1000" height="600" alt="A perfectly sized cat" />
<img class="mobile" src="/assets/chonker-600w.jpg"
width="600" height="300" alt="A perfectly sized cat" />
@media(max-width: 600px) {
.desktop { display: none; }
}
@media(min-width: 601px) {
.mobile { display: none }
}
……但这不太可扩展。比如说,如果我们处理的是 Markdown 文件,无法添加类名怎么办?或者我们想根据浏览器支持的不同格式(例如 JPEG 和 WEBP)进行切换怎么办?
这就是picture
元素发挥作用的地方。请看这个例子:
<picture>
<!-- List out all the WEBP images + WEBP sizes we can choose from -->
<source type="image/webp"
srcset="/assets/chonker-600w.webp 600w, /assets/chonker-1000w.webp 1000w"
sizes="100vw">
<!-- In case a browser doesn't support WEBP, fall back to this set of JPG sources -->
<source type="image/jpeg"
srcset="/assets/chonker-600w.jpg 600w, /assets/chonker-1000w.jpg 1000w"
sizes="100vw">
<!-- The actual, style-able img element that "receives" these sources -->
<!-- Also includes a default src in case no <source> can be applied -->
<img src="/assets/chonker-600.png" alt="A perfectly sized cat" />
</picture>
一些重要结论:
- 我们可以将图像标签包裹在 中,
picture
以解锁某种“切换”情况,让浏览器选择source
它能够渲染的第一个元素。但不可否认的是,大多数现代浏览器都能处理.webp
下面列出的这些文件,type="image/webp"
而无需 JPG 格式的后备方案(当前浏览器支持情况请见此处)。 - 每个源都有一个
srcset
属性,该属性接收给定图像格式的源 URL 列表。这些源以逗号分隔,并w
在末尾加上一个像素值 width 。然后,浏览器将根据该sizes
属性决定使用哪个源(下一节将详细介绍)。 - Picture 元素本身并非图像!当你开始尝试为这些图像添加样式时,这是一个有趣的陷阱。因此,你需要将所有与图像相关的 CSS(例如
object-fit
)放在该img
元素上,而不是picture
。
属性sizes
Sizes
是一个有趣的东西。它实际上看起来几乎和 CSS 一样,只是语法上有一些细微的差别。
还记得之前的那些辅助类吗?好吧,mobile
让我们做一些类似的事情。desktop
sizes
视频的要点:
一般来说,该sizes
属性是一种告诉浏览器对于给定的屏幕尺寸使用哪个图像的方式。
假设我们有一个横幅图像,它占据了移动用户屏幕的整个宽度,但我们有一个目录,它占据了500px
宽屏及以上宽度的一半。
戴上我们的 CSS 帽子,这意味着我们的图像位于100vw
下方(100% 屏幕宽度)500px
,50vw
当我们点击 时@media (min-width: 500px)
。这完美地转化为sizes
👉sizes="(min-width: 500px) 50vw, 100vw"
在元素的上下文中picture
:
<picture>
<!--stack up your media queries as sizes, delineated by commas ","-->
<source type="image/webp"
srcset="/img/6dfd7ac6-600.webp 600w, /img/6dfd7ac6-900.webp 900w..."
sizes="(min-width: 500px) 50vw, 100vw">
<img alt="Blue and purple cluster of stars" src="/img/6dfd7ac6-600.jpeg">
</picture>
根据经验,100vw
对于较小的设备,你应该将其作为“基本情况”,并根据布局的变化情况在顶部添加媒体查询。这意味着sizes
根据图像所处的上下文,情况会有所不同,所以如果你使用的是基于组件的框架,请务必注意这一点!
注意:您可能想知道为什么浏览器不能为我们完成所有这些工作。这归结于当您随处使用 CSS 时,“宽度”的不可预测性。如果您像我一样,倾向于使用大量百分比,例如width: 100%
用于图像块,这些百分比可能会根据所应用的容器、填充、边距等进行调整。如果浏览器在加载图像之前尝试解析所有这些样式,那么您将等待比预期更长的时间!
尊重高清显示器
请注意,屏幕的像素密度也会影响从给定 中选择哪张图片srcset
。对于高密度移动设备显示器,它实际上会选择一个大约是你指定宽度两倍的picture
图片!例如,我们有一个简单的声明,如下所示:
<picture>
<source type="image/webp"
srcset="/img/galaxy-600.webp 600w, /img/galaxy-1200.webp 1200w"
sizes="100vw">
</picture>
我们在这里使用100vw
,因此浏览器应该将图像源的宽度与显示器的宽度匹配。直观地讲,我们认为600px
宽屏显示器会接收/img/galaxy-600.webp
……但对于像 MacBook 或现代智能手机这样的高清显示器,它实际上会接收宽度为 600 x 2 像素的图像(/img/galaxy-1200.webp 1200w
在本例中)。所以,当你生成多个图像尺寸时,请始终使用较大的值 💡
🔨 使用 11ty image 将其应用到您的网站
好了,我们明白了picture
元素的用处……但它的强大程度取决于我们能提供给它的图片。我们真的想手动创建这些尺寸调整精美、优化过、多格式的图片吗?
幸运的是,有很多工具可以帮我们处理这个过程,我将重点介绍我发现的最简单的工具:11ty 的图像插件。
🚨 在你开始滚动到下一部分之前,我先声明一下,你不需要用 11ty 搭建网站才能使用这个工具。试用这个工具后,我发现它非常适合在任何场景下即时生成优化图片,而且不需要任何命令行技能🔥
生成优化图像
回家玩吧!说真的,放下手里的一切,打开你的代码编辑器🧑💻然后,创建一个新的目录/文件夹,并创建一个 basic package.json
。我们将安装@11ty/eleventy-img
依赖项:
mkdir woah-11ty-image-is-cool && cd woah-11ty-image-is-cool
npm init -y # Make a package.json with defaults for everything
npm i @11ty/eleventy-img
现在创建一个随机的 JavaScript 文件供我们玩玩(我称之为 mine )。在里面,只需粘贴11ty 文档image-generator.js
顶部的示例代码即可:
const Image = require("@11ty/eleventy-img");
(async () => {
let url = "https://images.unsplash.com/photo-1608178398319-48f814d0750c";
let stats = await Image(url, {
widths: [300]
});
console.log(stats);
})();
嗯,这看起来很简单。让我们从终端运行它,看看会发生什么:
node ./image-generator.js
如果幸运的话,你会看到几个新面孔出现:
- 一个包含两张图片的
/img
目录:一张 300 像素宽的星系 JPG 图片,以及一张大小相同的图片。注意它是如何与代码片段中的数组webp
匹配的👀widths
- 一个包含一些字符串的
/cache
目录。你可以把它想象成插件的自我提醒,提醒它我们下载了哪些图片。从互联网上下载图片的开销很大,所以为了避免每次运行脚本时都加载图片, 11ty 会检查缓存,看看我们之前是否已经加载过这张图片。
您还将看到控制台中记录了大量“统计信息”。大多数属性都是不言自明的,其中一些属性在我们picture
之前的演示中应该很熟悉(即sourceType
和srcset
属性)。我们甚至以字节为单位获取图像的输出size
,以便您检查格式和大小之间的差异。
等等,还有更多!让我们尝试不同的宽度和格式:
...
let stats = await Image(url, {
widths: [300, 1000, 1400],
formats: ['jpg', 'webp', 'gif']
});
...
我们应该在该目录中找到大量的分辨率。正如你所想象的,这对于我们之前的 picture 元素来说非常完美。你可以手动img
编写所有的source
s 和属性作为学习练习……size
自动化我们的图片元素
……或者让插件帮我们搞定!有了方便的数组stats
,11ty image 会把所有内容拼接成一个有效<picture>
元素。我们只需要调用一下generateHTML
辅助函数:
const Image = require("@11ty/eleventy-img");
(async () => {
let url = "https://images.unsplash.com/photo-1608178398319-48f814d0750c";
let stats = await Image(url, {
widths: [300, 1000, 1400]
});
const html = Image.generateHTML(stats, {
alt: "A blue and purple galaxy of stars", // alt text is required!
sizes: "100vw" // remember our training with "sizes" from earlier...
})
console.log(html);
})();
如果幸运的话,我们应该会看到一个picture
可以在我们网站的任何地方使用的美丽的东西:
<picture>
<source type="image/webp"
srcset="/img/6dfd7ac6-300.webp 300w, /img/6dfd7ac6-1000.webp 1000w,
/img/6dfd7ac6-1400.webp 1400w"
sizes="100vw">
<source type="image/jpeg"
srcset="/img/6dfd7ac6-300.jpeg 300w, /img/6dfd7ac6-1000.jpeg 1000w,
/img/6dfd7ac6-1400.jpeg 1400w"
sizes="100vw">
<img alt="A blue and purple galaxy of stars" src="/img/6dfd7ac6-300.jpeg" width="1400" height="1402">
</picture>
更进一步
这个插件还有很多额外的选项可以探索,比如
- 调整缓存选项以加快构建时间
- 同步生成图像统计数据 + 图片元素,因此您不必等待图像实际生成
- 微调夏普图像处理器,根据您的需要调整输出
📣 在任何框架中使用 11ty 镜像
如果所有这些<picture>
疯狂的想法让你兴奋不已,那就把这个 11ty 图片插件放到你自己的/assets
目录中吧!我写了个方便的小脚本,用来抓取目录中的所有图片(注意,不是递归的),并输出一些优化过的文件:
const Image = require('@11ty/eleventy-img')
const { readdir } = require('fs/promises') // node helper for reading folders
const { parse } = require('path') // node helper for grabbing file names
;(async () => {
const imageDir = './images' // match this to your assets directory
const files = await readdir(imageDir)
for (const file of files) {
const stats = await Image(imageDir + '/' + file, {
widths: [600, 1000, 1400], // edit to your heart's content
filenameFormat: (id, src, width, format) => {
// make the filename something we can recognize.
// In this case, it's just:
// [original file name] - [image width] . [file format]
return `${parse(file).name}-${width}.${format}`
},
})
console.log(stats) // remove this if you don't want the logs
}
})()
如果您恰好在个人网站上使用 11ty(或者至少想尝试一下),您picture
也可以自动插入元素。他们的指南picture
涵盖了如何构建您自己的“短代码”函数,以便为网站上每张未优化的图片插入正确的代码。
即使没有这种额外的功能,这个脚本对于任何基于 JS 的构建步骤来说都是一个很好的补充。下面是一个基本Image
组件,我可以根据上面的脚本将其添加到任何 React 应用中:
// consider using TypeScript for checking all these props!
const Image = ({ fileName, sizes, ...imageProps }) => (
<picture>
<source
type="image/webp"
srcSet={`/img/${fileName}-600.webp 600w, /img/${fileName}-1000.webp 1000w, /img/${fileName}-1400.webp 1400w`}
sizes={sizes}
/>
<source
type="image/jpeg"
srcSet={`/img/${fileName}-600.jpeg 600w, /img/${fileName}-1000.jpeg 1000w, /img/${fileName}-1400.jpeg 1400w`}
sizes={sizes}
/>
<img src={`/img/${fileName}-600.jpeg`} {...imageProps} />
</picture>
)
假设我的所有图像都按照这个文件命名约定生成(并且我的图像宽度始终为 600、1000 和 1400),这应该可以毫无问题地提取我们所有优化的图像👍
以下是将这些知识应用于以下方面的简要概述create-react-app
:
亲自尝试一下
您可以在此 CodeSandbox 🪄中查看create-react-app
+11ty 图像的运行示例
此版本还将在开发过程中监听新图像。欢迎您fork 源代码并在您自己的项目中尝试(并发现我不可避免地遗漏的一些极端情况 😉)。
Next、Nuxt、Gatsby 等的其他选项
尽管 11ty 图像很酷,但我还是应该强调一些流行元框架的“原生”选项:
- 对于 Next 来说,其内置的图像组件非常完美。它还能自动覆盖我们的尺寸、格式和图像压缩,此外还有一些简洁的道具,可以使用 来快速加载“首屏”图像
priority
。 - 对于 Nuxt,他们的
<nuxt-img>
和<nuxt-picture>
组件应该可以满足您的需求。它们提供与我们的 11ty 图片插件大部分相同的功能,允许您指定不同的格式、属性以及背景图片压缩。如果您想允许多种图片格式而不是仅一种,sizes
请务必使用!nuxt-picture
- 对于 Gatsby 来说,它拥有图像优化的黄金标准🏆他们的图像插件实际上是我几年前使用该框架的主要原因,而且它现在也越来越好了。最棒的功能(除了我们之前讨论的所有内容之外)是它们的图像加载动画。你可以淡入淡出图像的矢量轨迹,使用模糊效果等等。唯一的缺点是它需要加载到浏览器中大量的 JS 包来实现这一点,我在这里分享了我的一些看法。
- 除了框架之外,您还可以使用 Cloudinary 之类的工具进行远程优化。如果您不负责网站的构建过程,或者不想将图片存储在代码仓库中,那么 Cloudinary 是一个不错的选择。例如,您可以将所有 Wordpress 图片指向 Cloudinary 的 bucket,并从那里提取不同宽度和格式的图片。唯一的缺点是成本,因为 Cloudinary 会为您完成所有这些图片处理和存储工作。
学到一点东西吗?
很高兴听到这个消息!如果你想了解更多类似的通用 Web 开发解决方案,可以订阅 Web Wizardry 的新闻简报,每两周更新一些知识点。
文章来源:https://dev.to/bholmesdev/picture-perfect-image-optimization-for-any-web-framework-2o77