代码、游戏、以及有趣的故事
2025-01-24
通过 httparchive.org 的数据展示我们可以知道,当下网站大多数需求最多的资源类型还是图像,且它还比其他资源占用更多的带宽。
在图像居多的网站中,图像资源的加载对整体的用户体验有非常明显的影响,其中最常见的就是图片加载过慢。对此我们通常有这几种解决方案:图片优化,对图片进行压缩或者选择更加合适的图片格式、优化网络传输,如使用 CDN 加速以及优化图片的加载策略。
本文主要想聊的也就是「优化图片加载策略」
「图片优化」 和 「优化网络传输」 前者比较容易做到,后者多数情况都是靠砸钱优化的。
懒加载策略就是:推迟加载非视口内的图片,从而减少资源的请求数量。
目前主流的懒加载方案有以下几种:
img 标签 loading
从 chrome 76+ 版本开始,开发者可以使用 loading 属性来推迟加载,只有当页面滚动到离对应图片还有一定位置时才会进行加载。通过给 loading 属性设置 lazy 值,即可简单实现图片的懒加载。可以使用 caniuse.com 查询浏览器的兼容性支持。对于不支持 loading 属性的浏览器会自动忽略该属性,不会造成影响。
<img scr='image.jpg' loading='lazy' alt='测试图片'/>
Intersection Observer API
Intersection Observer API 通过异步观察目标元素与父级元素或顶级文档视口的位置变化。可以通过它来实现图片懒加载。
/*
<!-- 图片 DOM -->
<img data-src="image1.jpg" />
<img data-src="image2.jpg" />
*/
// 监听器配置
const config = {
// pass
}
// 创建监听器
const observer = new IntersectionObserver(function(item, self) {
item.forEach(({ isIntersecting, target }) => {
if(isIntersecting) {
if(target.dataset.src) {
target.src = target.dataset.src;
target.removeAttribute('data-src');
}
self.unobserve(target);
}
})
}, config);
// 绑定监听器
const images = document.querySelectAll("[data-src]");
images.forEach((image) => {
observer.observe(image);
})
scroll
因为 Intersection Observer API 是存在兼容性问题的,除了直接添加对应 polyfill 以外,我们还可以考虑降级为监听 scroll、resize、orientationchange 事件。具体实现思路和上面 Intersection Observer 差不多。具体点需要注意的是:计算图片节点与目标视口的距离,还需要配合使用节流函数,避免性能问题。
懒加载策略与懒加载相反:尽快地加载出图片,使得用户体验更为流畅。
link preload
使用硬编码 link 标签来实现预加载通常是最好的,但很可惜多数时候预加载的使用场景是动态的。
<link ref='preload' as='image' href='image1.jpg'/>
HTML 的 head 标签可以声明资源请求,指定页面需要预加载的资源,并且在浏览器的主要渲染机制启动之前加载,避免阻塞页面渲染且保证资源尽早可用。
动态加载
动态变化的场景下一般使用动态创建 img 标签或者动态发起 Ajax 请求。这种方法可能会导致资源加载顺序的问题,同时还有跨域问题需要注意。
// 动态加载图片
function loadImage(url){
return new Promise((resolve, reject) => {
let temp_img = new Image();
temp_img.src = url;
temp_img.onload = () => {
resolve("load success");
}
temp_img.onerror = () => {
reject("load error: " + temp_img.src);
}
temp_img = null;
})
}
不同的用户设备和网络都是不同的,通常移动设备上对网络的要求更高,如果图片资源无差别使用相同图片,可能造成带宽浪费或者是图片不清晰以及视觉体验差的问题,因此我们需要根据不同的设备加载不同的图片。
这种通常可以使用 picture 标签来定义多个不同的 source 节点和一个 img 节点,用于提供图片在不同设备或场景下的表现。
<!-- 为不同的媒体条件裁剪或修改图像。 -->
<picture>
<source srcset="image1_pc.png" media="(min-width: 990px)" />
<source srcset="image1_mobile.png" media="(min-width: 750px)" />
<img src="image1_def.png" alt="default" />
</picture>
<!-- 在支持的浏览器中优先使用更适合的图片格式 -->
<!-- 同时支持在有兼容性问题的浏览器中回退加载其他格式的图片。 -->
<picture>
<source srcset="image1.webp" type="image/webp" />
<source srcset="image1.jpg" type="image/jpeg" />
<img src="image1.jpg" alt="default" />
</picture>
<!-- 通过按需加载并显示最适合用户设备的图像,从而节省带宽和加快页面加载时间。 -->
<picture>
<source
srcset="image1_pc.png, image1_pc_2x.png 2x"
media="(min-width: 990px)"
/>
<source
srcset="image1_mobile.png, image1_mobile_2x.png 2x"
media="(min-width: 750px)"
/>
<img
srcset="image1_def.png, image1_def_2x.png 2x"
src="image1_def.png"
alt="default"
/>
</picture>
使用 HTTP/1.X 协议时,浏览器有同源最大并发连接数的限制,且 HTTP/1.X 不支持多路复用,因此一个多图站点想要获得较完整的视觉呈现,会有一定程度的延迟:所有的资源请求
如果不使用 HTTP/2 可以使用以下这几种方案: