前端图片加载优化的各种技巧

·  阅读 3093

目前的前端图片加载优化技术有很多,像懒加载/预加载,img上的srcset属性以及picture标签,新的图片编码格式以及Client Hints等。

Client Hints

顾名思义,client hints是指通过客户端信息来进行资源选择的一种方法,由于用在图片加载上,所以这里的客户端信息一般是指图片宽度,DPR(device pixel ratio)和视窗宽度(viewport width)。其基本原理是在http传输中加入相关header,让服务器好选择一张合适的图片返回给客户端。

如何使用

要使用client hints,需要在添加一个meta标签,内容如下:

<meta http-equiv="Accept-CH" content="DPR, Viewport-Width">
复制代码

然后当浏览器向服务器请求图片的时候,就会附带上两个额外的header:

DPR: 2
Viewport-Width: 535
复制代码

由此可知,客户端的像素密度是2,Viewport的宽度是535像素,这样服务器就能通过客户端信息来发送不同的图片了,这些图片既可以是已经存储的不同分辨率的图片,也可以即时地对图片大小进行转换,这取决于存储和计算之间的权衡。

兼容性

Cient Hints不是一个很新的东西,但它的兼容性却一般,可以作为一种渐进式用户体验的方法。 下面是它的浏览器兼容性:

image.png 可以看到,除了Chrome和Opera以及新版Edge之外全军覆没,所以这种方法见过的人少,用过的人更少了。下面就介绍一个一种更常用的方法。

Responsive images

如果说client hints是在服务器上对图片进行区分,那么Responsive images就是将这个过程放到浏览器上了。一般来说,组成Responsive images的标签和属性有img标签上的srcset,sizes属性,以及picture和source标签。

如何使用

关于srcset和sizes

理解这两者的最好方法就是用一个简单的例子,下面这个例子来自MDN:

<img srcset="elva-fairy-480w.jpg 480w,
             elva-fairy-800w.jpg 800w"
     sizes="(max-width: 600px) 480px,
            800px"
     src="elva-fairy-800w.jpg"
     alt="Elva dressed as a fairy">
复制代码

首先,sizes决定了一个叫插槽的东西,顾名思义,就是留给这个图片的空间是多少,假设我放置这个图片的容器是800px,需要填满这个容器,那么就应该写成sizes="800px",同时,sizes支持使用媒体查询来区分不同的slot,不同的slot使用逗号分开,如上面的写法,意思就是当viewport宽度大于600px的时候,slot宽度为800px,否则为480px,当没有图片刚好匹配slot宽度时,浏览器会选择第一张比slot宽的图片。

其次,srcset决定了可以使用的图片来源,如上面的写法,就是说浏览器可以按照slot的宽度从宽度分别为480px和800px的两张图片中自动进行选择。

最后,src属性是当浏览器不支持srcset时候进行回退使用。

需要注意的是,srcset每张图片后面跟的宽度单位是w,代表图片文件的实际宽度,也即img.naturalWidth的宽度,为什么要用原生分辨率呢,其实道理很简单,我们可以想一下如果图片后面跟着的是css pixel的话,那么实际上浏览器是无法知道最合适的图片的,因为不同设备的DPR不一样,假设当前设备DPR为1,srcset="elva-fairy-480w.jpg 480px",表明想在slot宽度为480px的时候使用这张图片,但如果另一台设备DPR为2,很显然图片就会糊了,这样就达不到想要的效果了。所以最好的方式是告诉浏览器图片的原始分辨率,让它通过DPR和slot宽度自行判断需要使用哪个图片。

不需要使用sizes的情况

实际上,sizes并不是必须的,我们知道img作为替换元素,在没有css规定的情况下,它的大小由内容(也就是图片本身决定),也就是说浏览器在实际加载完成图片之前,都是无法知道img占的位置大小的,所以才需要sizes这个属性显式地规定slot的宽度,也就是程序员期望加载完图片之后占的宽度。

但如果使用css规定了img的大小:

img {
  width: 300px;
  height: auto;
}
复制代码

这样的话,浏览器就能够知道img占的宽度,sizes也就变得不是必须了。

关于picture标签

picture标签实现的是根据媒体选择区分不同图片来源的功能:

<picture>
  <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg">
  <source media="(min-width: 800px)" srcset="elva-800w.jpg">
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva">
</picture>
复制代码

其中source标签标明可以选择的图片来源,浏览器会使用第一个匹配的source作为图片来源。如果像上面这样写,那么窗口宽度在大于等于800px的时候会使用第二个source,否则使用第一个source。

在最后一个source后面要加上一个img标签,一是为了在没有source匹配时有默认显示的图片,二是当浏览器不支持picture时可以平稳回退。

picture和srcset的区别

从上面的代码来看,picture和srcset实现了相似的功能,但实际上它们是很不一样的,从浏览器匹配的逻辑来看可以很明显地区分它们:

picture: media -> source
srcset: media -> slot -> source
复制代码

可以看到srcset匹配的过程中多了一个slot,也就是说srcset匹配的图片依据的是图片所占空间,而picture匹配依据的是屏幕宽度。

同时picture可以根据图片格式进行选择:

<picture>
  <source srcset="mdn-logo.svg" type="image/svg+xml">
  <img src="mdn-logo.png" alt="MDN">
</picture>
复制代码

如果浏览器不支持svg,那么就会使用img中的png图像。

兼容性

image.png

从兼容性来看,Responsive images明显比Client Hints好的多,除了不争气的IE以外大部分新浏览器都支持。

图片格式

图片格式是优化图片加载的重要方式之一,除了常用的png/jpeg图像之外,现在常用的高压缩比+高质量的图片编码格式有WebP和AVIF等。

WebP

webp是Google搞得一种基于vp8视频帧编码的图片编码格式,前期兼容性一般,现在除了IE和safari之外的新浏览器大部分都支持:

image.png

AVIF

AVIF由开源组织AOMedia开发,Netflix、Google与Apple均是该组织的成员

有大公司撑腰,AVIF可以说前景比较光明,但其目前的原生兼容性不佳:

image.png

不过好在其有一个polyfill:Kagami/avif.js

这个polyfill使用service worker劫持fetch事件然后对avif图片进行处理,所以页面中的img标签可以直接引用avif文件:

<body>
  <!-- Register worker -->
  <script src="reg.js"></script>

  <!-- Can embed AVIF with IMG tag now -->
  <img src="image.avif">

  <!-- Or via CSS property -->
  <div style="background: url(image2.avif)">
    some content
  </div>
</body>
复制代码

当然,前提是需要浏览器支持service worker。

好了,文章先写到这里,休息一下~

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改