likes
comments
collection
share

图片懒加载+新属性+picture

作者站长头像
站长
· 阅读数 46

1.为什么要做图片懒加载

一般网页图片所占用资源比例较大,因此在做网页性能优化时,图片优化排在首要位置,而图片懒加载则是图片优化中的基础。当一个页面中存在大量图片时,图片懒加载则是必要任务。对于首屏之外的内容,特别是图片等若是全部加载完,即费时又费力。实现懒加载可以降低首屏加载时间,提高用户体验。

2.图片懒加载原理

最简单的理解:窗口内可见的图片才进行加载。图片初始的src赋值一个很小的默认图片,例如很小的灰色图片或者加载图片,当图片进入窗口时将真正的图片链接替换到src上。

注意: 一般首屏图片不会进行图片懒加载

如下图: 从进入窗口开始,图片进行src赋值处理(当然可以做提前处理,留一个缓冲区)

图片懒加载+新属性+picture 图片来源: developer.mozilla.org/zh-CN/docs/…

图片懒加载+新属性+picture 图片来源: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)

  1. 首先获取窗口的固定高度 clientHeight
  2. 再获取元素相对窗口的位置
  3. 最好留一个缓冲区域
  4. 判断元素是否是展示状态,(可选)
    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.浏览器级别的网络图像懒加载

在这个浏览器加载属性出现前,我们一般使用下面两种方法:

若浏览器支持加载属性,建议使用浏览器加载属性。

浏览器加载属性具体细节参考: 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库。

浏览器加载属性特点:

  1. 浏览器级的懒惰加载也确保了即使在客户端禁用了JavaScript,图像的延迟加载仍然有效。
  2. Chrome以不同的优先级加载图像,这取决于它们相对于设备视口的位置。低于视口的图像会以较低的优先级加载,但它们仍然会被尽快获取。(存在缓冲区)
  3. 不支持的浏览器不回有任何影响
  4. 使用简单, 如下:在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.兼容处理

实现优先级:

  1. 若浏览器支持原生loading="lazy"则使用原生浏览器懒加载;
  2. 若浏览器支持IntersectionObserver属性,则通过该属性实现;
  3. 否则使用原生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组件全局使用。

参考: web.dev/browser-lev…

转载自:https://juejin.cn/post/7235074521778176037
评论
请登录