如果一个网页中包含比较多的图片,在页面加载时,会严重拖慢整个页面的加载速度,这就是我们今天要解决的问题.

这篇文章聊一下如何去优化图片,提高页面的加载速度.文章中演示的例子,只是将几张大图片简单的展示出来,通过这个例子去展示如何优化.

例子的源码在 github.com/XYShaoKang/… 中,可以克隆到本地进行调试,在线预览地址 xyshaokang.github.io/image-optim…

先看下优化前后的对比:

上面一个是优化前的,下面是优化后的,为了对比效果,将速度限制为10Mbit/s

优化前后对比

可以看很明显的对比,优化前需要将近 20s 的时间,而优化后只用了不到 1s 的时间

接下来介绍是如何一步步优化的

生成不同尺寸的图片

在页面中展示的图片一般都比需要展示的宽度要大,比如一张 1080px 宽的图片,可能只需要展示在 400px 的框内,没有必要直接使用原图,可以使用原图生成一张 400px 宽的图片,这样可以在不损失画质情况下节省带宽,提升加载速度.

另外很多时候我们需要做响应式布局,可能在一种布局下,需要展示 200px 宽度的图片,而在另外一种布局下,需要展示 600px 宽度的图片,这个时候就需要生成多种尺寸的图片.

所以我们可以提前定义一些尺寸范围的预设,将所有图片生成生成预设中对应尺寸的图片,然后在使用时根据实际要展示的宽度,去加载对应范围宽度的图片.

在例子中我所使用的预设

const widths = [40, 200, 400, 800, 1200, 1600]

然后使用 sharp 生成指定尺寸的图片

/**
 * 生成指定宽度的图片
 * @param input 原图路径
 * @param output 目标路径
 * @param width 目标宽度
 * @returns
 */
function resizeImage(input: string, output: string, width: number) {
  return sharp(input).resize(width).toFile(output)
}

如果有将代码克隆到本地,可以运行pnpm cli(如果是 npm 使用npm run cli,如果是 yarn 可以使用yarn cli)来查看实际生成的图片,生成的图片在 dist/image

详细源码,可以查看 cli.ts

通过设置 img 的 srcSet 属性来根据不同的宽度使用对应大小的图片

srcSet 是以逗号分隔的多个字符串表示可用的图像,每个字符串是由空格分隔的前后两部分,后面是当满足条件时使用这个图片,前面则是图片的地址,关于 srcSet 更具体的描述,可以查看 MDN 中的介绍 img: srcset

这里我们指定预设好的尺寸以及对应的链接,这样在加载时,会根据当前展示图片需要的宽度,加载对应尺寸的图片.

const Image: FC = () => {
  const srcSet = `image/cat-young-animal-curious-wildcat-200.jpg 200w,
                  image/cat-young-animal-curious-wildcat-400.jpg 400w,
                  image/cat-young-animal-curious-wildcat-800.jpg 800w,
                  image/cat-young-animal-curious-wildcat-1200.jpg 1200w,
                  image/cat-young-animal-curious-wildcat-1600.jpg 1600w`

  return <img srcSet={srcSet} />
}

实际效果,可以看到随着需要展示的图片不断变大,浏览器会加载我们在 srcSert 中定义的对应的图片.

响应式加载图片

渐进式加载

如果需要展示的图片的宽度比较大时,需要加载比较大尺寸的图片,可能需要一些时间加载,图片的位置就会显示白色,体验比较差.可以通过渐进式加载的方式让体验变的更好.

具体的做法是,不直接去加载大尺寸的图片,而是先加载一个小尺寸的图片(比如我们上面预设的尺寸中 40px 宽的图片就派上用场了),然后将小图片放大充当图片的占位符(小尺寸图片可以很快加载完成,这时页面是已经有图片,虽然比较糢糊,但至少会比一块空白让用户体验更好),然后在后台加载大尺寸的图片,等加载完成之后,将小图片替换成大图片.

通过设置filter: blur(20px);可以让小图像看起来不至于都是像素点,反而有一丝朦胧的感觉.

在加载完成时,加上一点糢糊的过度,图片从糢糊慢慢变清晰的过程,符合用户在等待过程中,希望图片逐渐被下载的预期.

渐进式加载图片

图片组件的详细源码请查看 Image.tsx

懒加载

懒加载是通过延迟不可见图片的加载,从而避免不必要的下载.

要实现懒加载,只需要在 img 标签加上 loading="lazy" 即可开启原生支持的懒加载.

不过什么样的图片会延迟加载,什么情况下回去加载图片,需要看浏览器具体的实现.更多信息可以看后面的参考资料

后记

本篇文章讲解了如何一步步去优化网页中的图片,优化的步骤:

  • 生成不同尺寸的图片
    • 通过 sharp 来生成预设尺寸的图片
  • 通过设置 img 的 srcSet 属性来根据不同的宽度使用对应大小的图片
  • 渐进式加载
    • 生成小图片用作占位符
    • 糢糊图片 blur
  • 图片懒加载

当然还有其他可以优化的点,比如调整 sharp 的参数输出更小的图片,以及压缩率更高的图片,通过 CDN 来提升图片的下载速度等等,感兴趣的同学可以自行深入研究.

本文主要是研究 gatsby-plugin-image 如何实现的过程中,总结而来.

参考资料

  • gatsby-plugin-image
  • The “Blur Up” Technique for Loading Background Images
  • Why You Should Use Picture Tag Instead of Img Tag
  • Responsive images
  • Progressive loading
  • 延迟加载
    • Browser-level image lazy-loading for the web
    • 浏览器 IMG 图片原生懒加载 loading=”lazy”实践指南
    • Fast load times

转自:https://juejin.cn/post/7033780851053690894