概述:因为内部项目运营工具中有遇到处理大量图片在一瞬间请求的场景,在开发阶段遇到了如果不处理图片,不用懒加载的话,浏览器会在一瞬间造成卡顿,用户体验和效果非常差。

解决方案

# 未调研的 React 三方懒加载库

# img 图片的属性 loading

因为项目采用的是 umi+ts,而 img 属性的 loading 并未标准化,还处在实验阶段,但是 Chrome 76+ 已经支持。代码中直接在 jsx 中为img 标签加上 loading="lazy" 属性之后会报错,是因为 ts 的类型中 img 不存在 loading 的属性,解决办法是声明一个类型文件,将需要设置的标签属性继承到 HTML 属性就好了,别的标签也类似。

// react-unstable-attributes.d.ts
import "react";

declare module "react" {
  interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
    loading?: "auto" | "eager" | "lazy";
  }
}

// 参数
lazy:延迟加载。
eager:立即加载。
auto:由浏览器来决定是否延迟加载。

但是在直接为图片加了 loading 属性之后并没有实现懒加载,后来在调试的时候意外为图片加了 width 尺寸之后发现竟然生效了,或许在真实场景之下还需要微调。

<img loading="lazy" width="90px" />

loading="lazy" 的属性对于别的浏览器的兼容一般,可以利用 js 判断,不支持 loading 的话使用IntersectionObserver优雅降级。可参考 (opens new window)

# IntersectionObserver

IntersectionObserver 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。它会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 视口 ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。摘自 MDN (opens new window)

兼容的 folyfill intersection-observer

伪代码

// 注册一个回调函数 满足条件的时候自动执行为图片src赋值
const observer = new IntersectionObserver(function(changes) {
  changes.forEach(function(element, index) {

    // 当这个值大于0,说明满足我们的加载条件了,这个值可通过rootMargin手动设置
    if (element.intersectionRatio > 0) {
      // 设置图片src属性来加载
      element.target.src = element.target.dataset.src;

      // 放弃监听,防止性能浪费。
      observer.unobserve(element.target);
    }
  });
});

function initObserver() {
  [...document.querySelectorAll('.list-item-img')].forEach(item => {
    // 对每个list元素进行监听
    observer.observe(item);
  });
}

useEffect(() => {
  initObserver()
}, [])

// jsx
<img class="list-item-img" data-src="1.png"/>
<img class="list-item-img"  data-src="2.png"/>
最后更新: 2/12/2023, 7:42:22 AM