使用 WebP 优化您的网站:减小图像文件大小,提升网站性能
太长不看
图像.webp格式可以显著减小图像文件的大小……我指的是文件大小最多可减少 90%(与它们的.jpg对应.png格式相比)。除了 Safari 和 IE 之外,.webp图像格式得到了广泛的浏览器支持,而且借助Sharp等工具,您可以轻松地将.jpgS 和.pngS 格式的图片集合转换为.webp图像,以便在支持这种格式的浏览器上显示。检测不支持这种格式的浏览器相对简单.webp,我将向您展示一种处理这些浏览器的方法。
该示例的代码可以在 GitHub 上找到。
背景故事
我是一名在图形/印刷行业工作的软件工程师,所以经常要处理图像。我开发的大多数应用程序都涉及大量图像(用户上传和编辑图像,以及显示大量用户上传的内容)。优化图像以适应网络显示(减少带宽占用)对我们大有裨益,但更重要的是,它可以显著提升网站性能和页面加载速度(因为发送给用户的数据量大大减少)。最近,我开始.webp尽可能地使用网络优化,接下来我会分享一些我总结的技巧。
想跟着一起做吗?
如果你想跟着做,可以查看GitHub 上的代码。你可以克隆这个仓库,然后npm install从文件夹的根目录运行命令(只需要一个 node 模块,但它很重要)。
如何创建.webp图像?
干脆利落。就是这样。
很有可能你从未收到过.webp图片文件,或者从未用图像处理软件打开过图片文件。那么,.webp在网站上使用图片是不是非常麻烦呢?起初,我也是这么想的。但事实证明,并没有那么糟糕。
由于我经常处理用户上传的大型图像文件,所以我很习惯将图像转换为适合网页显示的版本。我选择的后端技术是 Node,幸运的是,Node 上有一个非常棒的图像处理库:Sharp。Sharp的图像处理速度极快,几毫秒内即可完成照片的调整大小和裁剪。它对我来说简直是救星。
你可以使用 sharp 处理各种图像类型(例如.jpgJPEG、 JPEG .png、.tiffJPEG 等),对图像进行处理,并以多种格式输出,包括 JPEG .webp。我通常使用 sharp 将用户上传的图像转换为网页格式,但它在编写脚本处理自己的文件方面也同样有用。接下来,我将向你展示如何使用 sharp 复制.webp网站图像。
如果您打开了我的示例仓库,您可以看到一个名为 `<filename>` 的文件夹/images,其中包含一些图像。只有 `<images>` 和dog.jpg`<images> ` 是“原始”图像chair.png。我们将生成其他文件。
还有一个名为process-images.js 的文件,其内容如下:
// Import sharp (processing images) and path (traversing directory)
const sharp = require('sharp');
const path = require('path');
// Create an asynchronous IIFE
(async function(){
// Where are our image files located?
const imageDirectory = path.join(__dirname, './images');
// Which images should we process?
const imageNames = ["dog.jpg", "chair.png"];
// What size should we crop to?
const maxSize = 1000;
// Loop through the images and process them one at a time.
for (let imageName of imageNames) {
try {
// Start by creating a jpg version
await sharp(path.join(imageDirectory, imageName)) // This inputs the file into sharp
.resize(maxSize, maxSize, { fit: "inside" }) // This resizes our image
.toFile(
path.join(imageDirectory, imageName.replace(/\.(jpg|png)$/, `_${maxSize}$&`)) // Replace file extensions with .jpg (assumes .jpg or .png)
); // This writes the new image.
// Same thing, but create a .webp version
await sharp(path.join(imageDirectory, imageName))
.resize(maxSize, maxSize, { fit: "inside" })
.toFile(
path.join(imageDirectory, imageName.replace(/\.(jpg|png)$/, `_${maxSize}.webp`)) // Replace file extensions with .webp (assumes .jpg or .png)
); // This writes the new image.
} catch (_) {}
} // End loop
process.exit();
})();
这段脚本会读取我们的“原始”图像文件,并创建.webp它们的多个版本。以下是该文件的功能:
我们导入了pathNodesharp模块(path这是一个原生模块)。然后,我们将运行一个函数来处理文件。在该函数的顶部,你会看到:
// Where are our image files located?
const imageDirectory = path.join(__dirname, './images');
// Which images should we process?
const imageNames = ["dog.jpg", "chair.png"];
// What size should we crop to?
const maxSize = 1000;
这会设置一些要使用的值,例如文件的存储位置(imageDirectory变量)、要处理的图像文件(imageNames数组)以及裁剪后的尺寸(maxSize)。然后,我们将遍历数组中的每个文件imageNames并进行处理。
我们先从调整“原始”图像的大小开始:
await sharp(path.join(imageDirectory, imageName)) // This inputs the file into sharp
.resize(maxSize, maxSize, { fit: "inside" }) // This resizes our image
.toFile(
path.join(imageDirectory, imageName.replace(/\.(jpg|png)$/, `_${maxSize}$&`))
); // This writes the new image.
这会将图像输入到 Sharp 中,告诉 Sharp 调整图像大小,然后输出文件。可怕的表达
imageName.replace(/\.(jpg|png)$/, `_${maxSize}$&`)
只是告诉 sharp 在文件扩展名前添加“_1000”,所以dog.jpg会变成dog_1000.jpg,chair.png也会变成chair_1000.png。
我们将运行类似的流程,但会.webp在文件中添加扩展名。Sharp 会自动将该文件写入 .ssh 文件.webp——这就是神奇之处。对于每个“原始”文件,我们都应该在同一个文件夹中得到一个裁剪后的版本和一个裁剪后的.webp版本。
脚本编写完成后,我们需要从命令行运行以下命令:
node process-images.js
处理文件就这么简单!作为一项有趣的扩展功能,您可以轻松地扩展该脚本,为每张图片创建多种不同尺寸的版本(例如,一个用于缩略图,一个用于“主图”)。
使用我们的新图像
大多数浏览器都支持.webp图片,但 Safari 和 IE 不支持。我认为 Safari 的市场份额足够大,因此有必要为不支持.webp图片的浏览器提供备用方案(我尽量忽略 IE,但这个方案也应该能兼容 IE)。为了便于说明,我将展示一个简单的 Vue “应用”,它会.webp在可能的情况下显示图片,并在必要时回退到.jpg其他格式.png。
在代码仓库中,你会找到一个名为index.html 的文件,其中包含非常简洁的HTML 代码和少量 Vue 代码,用于演示如何插入.webp图片。该index.html文件包含一小段 HTML 代码:
<div id="app">
<h1>Webp supported: {{ webpSupported ? 'Yes' : 'No' }}</h1>
<!-- Show the chair photo -->
<img
:src="transformImgExt('/images/chair_1000.png')"
width="150px"
/>
<!-- Show the dog photo -->
<img
:src="transformImgExt('/images/dog_1000.jpg')"
width="150px"
/>
</div>
标签img是我们用来展示新图片的地方。如果您不熟悉 Vue.js,:src图片标签的 `<image>` 属性告诉 Vue 我们希望使用src给定值的动态属性。我们将编写一个函数transformImgExt,该函数接收一个图片 URL,并.webp在适当的时候将其替换为相应的版本。例如,` <image>` 会返回图片transformImgExt('/images/chair_1000.png')的相对 URL ,但如果浏览器支持图片,则会/images/chair_1000.png尝试将其替换为 ` <image>`。/images/chair_1000.webp.webp
检测浏览器支持情况
让我们深入研究一下检测是否支持 Vue.js 所需的 JavaScript 代码.webp。以下是文件中的 JS 代码index.html。(如果您不熟悉 Vue.js,不必过于担心细节。)
let app = new Vue({
// What should we mount our Vue instance to?
el: "#app",
// App data
data: {
// We'll initially assume webp is supported
webpSupported: true
},
// Methods
methods: {
/**
* Helper to transform image extension.
* Checks if webp is supported, and will swap out the image extension accordingly.
*/
transformImgExt (url) {
// If webp is supported, transform the url
if (this.webpSupported) {
return url.replace(/\.\w{1,5}$/, ".webp");
} else { // Otherwise, just return the original
return url;
}
}
},
/**
* When app is "created", we'll run some checks to see if the browser supports webp
*/
created() {
(async () => {
// If browser doesn't have createImageBitmap, we can't use webp.
if (!self.createImageBitmap) {
this.webpSupported = false;
return;
}
// Base64 representation of a white point image
const webpData = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoCAAEAAQAcJaQAA3AA/v3AgAA=';
// Retrieve the Image in Blob Format
const blob = await fetch(webpData).then(r => r.blob());
// If the createImageBitmap method succeeds, return true, otherwise false
this.webpSupported = await createImageBitmap(blob).then(() => true, () => false);
})();
} // End created
})
您会data在代码中看到一个属性:
// App data
data: {
// We'll initially assume webp is supported
webpSupported: true
}
这是我们的“应用程序”状态。我们将创建一个名为 `supported` 的状态属性,webpSupported用于保存一个布尔值,指示我们是否支持 `images` 。一旦我们进行一些“嗅探”以确认浏览器是否能够处理这些图像,.webp我们可能会更改此值。.webp
接下来,我们直接跳到以下created()部分:
/**
* When app is "created", we'll run some checks to see if the browser supports webp
*/
created() {
(async () => {
// If browser doesn't have createImageBitmap, we can't use webp.
if (!self.createImageBitmap) {
this.webpSupported = false;
return;
}
// Base64 representation of a white point image
const webpData = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoCAAEAAQAcJaQAA3AA/v3AgAA=';
// Retrieve the Image in Blob Format
const blob = await fetch(webpData).then(r => r.blob());
// If the createImageBitmap method succeeds, return true, otherwise false
this.webpSupported = await createImageBitmap(blob).then(() => true, () => false);
})();
} // End created
这段代码使用了我在这篇文章中找到的一种技术。它会检查浏览器是否支持该createImageBitmap方法——如果没有,.webp则表示不支持。然后,我们会创建一个 base64 编码的 webp 图像作为 blob,并尝试从中创建图像位图。如果成功,则说明浏览器支持该方法.webp。这背后有一些技术细节,但超出了本文的讨论范围。
在代码片段的末尾,你会注意到这样一句话:
this.webpSupported = await createImageBitmap(blob).then(() => true, () => false);
语句右侧尝试创建图像位图,如果创建成功,则() => true执行函数表达式(返回 true true),否则() => false执行函数表达式(返回 false false)。这为我们提供了 `image` 的值this.webpSupported,它引用了我们之前查看的应用程序状态属性。此时,我们的 ` data.webpSupportedimage` 属性包含一个布尔值,该值实际上告诉我们浏览器是否支持.webp图像。
我们还有最后一件事要看:transformImgExt方法:
/**
* Helper to transform image extension.
* Checks if webp is supported, and will swap out the image extension accordingly.
*/
transformImgExt (url) {
// If webp is supported, transform the url
if (this.webpSupported) {
return url.replace(/\.\w{1,5}$/, ".webp");
} else { // Otherwise, just return the original
return url;
}
}
此方法接受一个 URL,如果.webp支持,它会将文件扩展名替换为.webp. 。否则,它只会返回 URL。
我们来检查一下这条线。
return url.replace(/\.\w{1,5}$/, ".webp");
不过,我们再深入探讨一下。如果您不熟悉 JavaScript 中的正则表达式,这看起来可能像是一些随机字符。我们使用的是字符串的“replace”方法。这/\.\w{1,5}$/是一个用于查找文件名扩展名的正则表达式。\.开头的“.”表示一个句点,后面\w{1,5}会查找 1 到 5 个字母(可能是单词字符),$结尾的“.”表示它应该位于字符串的末尾。如果我们找到匹配项,我们会将其替换为“.webp”。这样,应该会将“.jpg”或“.png”替换为“.webp”。(注意!这也会将“.pdf”转换为“.webp”。如有需要,您可以进一步优化匹配规则。)
现在,我们可以尝试用它transformImgExt来提供.webp图像扩展名,前提是我们的浏览器支持。我们之前已经见过这种情况了。
观看实际演示
我把这些资源放在 CodePen 里来展示。(图片 URL 不同是因为它们是通过 CodePen 上传的。)如果你在 Chrome、Firefox 或 Edge 浏览器中打开这个示例,应该可以看到图片确实是 .png 格式的.webp。右键点击其中一张图片并检查属性,或者在新标签页中打开图片,你会发现它确实是.webp.png 格式的。如果你在 Safari 或 IE 浏览器中打开这个示例,应该会看到图片是.png.jpg或.png.png 格式的。
我们节省了多少钱?
文件压缩方式.webp非常巧妙,但具体节省的空间因图像而异。我们来看这个例子。
dog_1000.jpg大小为 122 KB,dog_1000.webp大小为 90 KB。节省了 25%。不错!chair_1000.png大小为 778 KB。chair_1000.webp大小为81KB。节省了 89.5%。这太棒了。
实际使用中,我平均能节省 40% 到 60% 的费用。.png图片似乎最能节省费用,而且.webp图片支持透明度!
结束语
.webp很棒。使用它们可以节省大量文件大小。不过,将它们集成到网站中需要一些工作。以下是一些相关的补充说明。
- 使用 sharp,您可以控制“到 webp”转换的各个方面,例如是否要进行无损压缩等等。
- 夏普的转换速度非常快,所以进行这类转换的成本很低。
- 我一直在使用 WebP 格式处理静态网站资源(如上图所示),也处理用户上传的文件。通常,当用户上传文件时,我会创建一个调整大小后的
.jpg版本,以及一个调整大小后的WebP 版本.webp。Sharp 工具可以以极快的速度撕下这些文件。 - 我通常会创建一个类似
transformImgExt上面所示的方法,并将其公开到我的应用程序中,以便在任何需要显示.webp图像的地方使用它。这样可以提高方法的可重用性,而且将新的图像格式集成到应用程序中也并不需要太多工作(同时提供对 Safari 和 IE 的回退支持)。