图片懒加载+新属性+picture
1.为什么要做图片懒加载
一般网页图片所占用资源比例较大,因此在做网页性能优化时,图片优化排在首要位置,而图片懒加载则是图片优化中的基础。当一个页面中存在大量图片时,图片懒加载则是必要任务。对于首屏之外的内容,特别是图片等若是全部加载完,即费时又费力。实现懒加载可以降低首屏加载时间,提高用户体验。
2.图片懒加载原理
最简单的理解:窗口内可见的图片才进行加载。图片初始的src赋值一个很小的默认图片,例如很小的灰色图片或者加载图片,当图片进入窗口时将真正的图片链接替换到src上。
注意: 一般首屏图片不会进行图片懒加载
如下图: 从进入窗口开始,图片进行src赋值处理(当然可以做提前处理,留一个缓冲区)
图片来源: developer.mozilla.org/zh-CN/docs/…
图片来源:blog.csdn.net/weixin_4387…
3.如何实现原生js懒加载
3.1如何监控图片进入窗口?
通过scroll事件,最好再加一个节流(这里节流函数直接使用了vueuse库)
const lazyThrottleFn = useThrottleFn(lazyFunc, 200);
document.addEventListener("scroll", lazyThrottleFn);
3.2如何计算图片进入窗口?
(这里只针对竖向滚动,横向滚动比较少,如果遇到可以参考处理。同理还有resize, or orientationchange)
- 首先获取窗口的固定高度 clientHeight
- 再获取元素相对窗口的位置
- 最好留一个缓冲区域
- 判断元素是否是展示状态,(可选)
const target = item as HTMLElement;
const clientHeight = window\.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const rect = target.getBoundingClientRect();
const targetBottom = rect.bottom;
const targetTop = rect.top; const HEIGHT = 50; // 缓冲加载距离50
// 判断图片是否进入视口
if ( targetTop <= clientHeight + HEIGHT && targetBottom >= 0 && getComputedStyle(target).display !== "none" ) {
// 处理
}
3.3核心代码实现
- 默认图片链接放在src属性,真实图片链接放在data-src属性上;
- 赋值后移除元素懒加载类名, 防止重复处理;
- 全部元素处理后,移除触发(scroll等)事件
<img src="默认图片.png" data-src="真实图片.png" class="lazy" alt="logo">
const lazyThrottleFn = useThrottleFn(lazyFunc, 200);
document.addEventListener("scroll", lazyThrottleFn)
function lazyFunc() {
const lazyArr = [].slice.call(document.querySelectorAll(".lazy"));
lazyArr.forEach((item, index) => {
const target = item as HTMLElement;
const clientHeight =
window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight;
const rect = target.getBoundingClientRect();
const targetBottom = rect.bottom;
const targetTop = rect.top;
const HEIGHT = 50; // 缓冲加载距离50
// 判断图片是否进入视口
if (
targetTop <= clientHeight + HEIGHT &&
targetBottom >= 0 &&
getComputedStyle(target).display !== "none"
) {
const dataSrc = target.getAttribute("data-src");
if (!dataSrc) return;
target.setAttribute("src", dataSrc);
target.classList.remove("lazy"); // 取消监控
lazyArr.splice(index, 1);
if (lazyArr.length === 0) {
document.removeEventListener("scroll", lazyThrottleFn);
}
}
});
}
4.浏览器级别的网络图像懒加载
在这个浏览器加载属性出现前,我们一般使用下面两种方法:
- 原生js
- Intersection Observer API属性,下面有介绍 Intersection Observer API属性的实现。
若浏览器支持加载属性,建议使用浏览器加载属性。
浏览器加载属性具体细节参考: web.dev/browser-lev…
下面做简要介绍:
Browser-level image lazy loading for the web
In Chrome 76 onwards, you can use the loading attribute to lazy-load images without the need to write custom lazy loading code or use a separate JavaScript library.
在Chrome 76以后,你可以使用加载属性来懒惰地加载图片,而不需要编写自定义的懒惰加载代码或使用单独的JavaScript库。
浏览器加载属性特点:
- 浏览器级的懒惰加载也确保了即使在客户端禁用了JavaScript,图像的延迟加载仍然有效。
- Chrome以不同的优先级加载图像,这取决于它们相对于设备视口的位置。低于视口的图像会以较低的优先级加载,但它们仍然会被尽快获取。(存在缓冲区)
- 不支持的浏览器不回有任何影响
- 使用简单, 如下:在img标签上增加该属性 loading="lazy"
<img src="image.png" loading="lazy" alt="…" width="200" height="200">
注意: 为了使浏览器能够在页面上为图像保留足够的空间,建议所有图片尽可能设置width和height, 即使不做图片懒加载。
兼容: 不支持的浏览器不会有任何影响,下面会对兼容情况进行处理,可以通过caniuse网站查看现在的支持情况。
5.借助 Intersection Observer API属性完成
现在该属性的兼容性已比较好了,可以通过它来检查元素的可见性,每当因页面滚动或者窗口尺寸发生变化,使得元素与设备视口或与其他指定元素产生交集时,会触发通过 Intersection Observer API配置的回调函数。
通过IntersectionObserver api判断图片是否出现在可视区域内,性能和效率都比较好
这里直接使用了vueuse库中的useIntersectionObserver。
const targetIsVisible = ref(false);
function LazyObserver() {
const lazyArr = [].slice.call(document.querySelectorAll(".lazy"));
lazyArr.forEach((item, index) => {
const target = item as HTMLElement;
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }], observerElement) => {
targetIsVisible.value = isIntersecting;
if (!targetIsVisible.value) return;
const dataSrc = target.getAttribute("data-src");
if (!dataSrc) return;
target.setAttribute("src", dataSrc);
target.classList.remove("lazy"); // 取消监控
lazyArr.splice(index, 1);
if (lazyArr.length === 0) {
document.removeEventListener("scroll", lazyThrottleFn);
}
},
{
rootMargin: "10px",
}
);
});
}
6.兼容处理
实现优先级:
- 若浏览器支持原生loading="lazy"则使用原生浏览器懒加载;
- 若浏览器支持IntersectionObserver属性,则通过该属性实现;
- 否则使用原生js执行懒加载(scroll 事件等);
function lazyLoad() {
if ("loading" in HTMLImageElement.prototype) {
const lazyArr = document.querySelectorAll(".lazy");
// https://web.dev/browser-level-image-lazy-loading/
lazyArr.forEach((item) => {
const dataSrc = item.getAttribute("data-src");
if (!dataSrc) return;
item.setAttribute("srcset", dataSrc);
item.classList.remove("lazy"); // 取消监控
});
} else if ("IntersectionObserver" in window) {
LazyObserver();
} else {
document.addEventListener("scroll", lazyThrottleFn);
}
}
7.结合picture实现
<picture>
<source class="lazy" :srcset="lazyUrl" :data-src="url" />
<img :src="url" :style="{ width, height, objectFit }" loading="lazy" alt="img" />
</picture>
js处理时,替换source中的srcset即可
const dataSrc = target.getAttribute("data-src");
if (!dataSrc) return;
target.setAttribute("srcset", dataSrc);
target.classList.remove("lazy"); // 取消监控
8.拓展
借助picture,同步实现响应式图片等。建议封装Image组件全局使用。
转载自:https://juejin.cn/post/7235074521778176037