一分钟实现图片懒加载

916 阅读2分钟

背景

在一些图片资源比较丰富的场景下,如果没有被用户观察到的图片也直接去加载的话,页面往往会变得十分卡顿,像下面这样,一次性加载上千上图片,显而易见的卡顿会给用户极其不好的体验。

02.gif

因此我们需要通过懒加载,也称为延迟加载,在图片滚动到用户可视区域内一定范围的时候去加载,从而有效提高用户体验、减少无效资源的请求。

01.gif

实现方案

实现逻辑也很简单

1、将图片真实的url挂载到img元素的自定义属性上data-xxx,url为空或者给定一个预设图片。

2、监听图片是否出现在用户的可视区域内。

3、出现在可视区域内后更新url属性为真实图片地址。

第一步和第三步较为常规,不加以赘述。

第二步的传统实现方案,大多数是以下步骤

  • 找到列表容器

  • 监听scroll事件,对比容器的高度、滚动高度、和图片距离容器顶部的高度,判断是否滚动到可视区域

  • 更新图片真实src

期间涉及到比较繁琐的dom元素属性计算以及节流等操作,一分钟不太好实现,不太契合文章标题。

因此,引入一下文章的主角:IntersectionObserver(话说MDN的新UI还没看习惯)

IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。

换句话说,我们可以通过它来实现“观察”图片是否出现可视区域内。

对于每张图片
  //... itemRef挂载到单张图片元素上
  const cb = (entrys) => {
    if (!itemRef.current) return
    // enterys是实例观察的对象数组,其中target为对应的的dom元素,isIntersecting为交叉状态,即是否出现在了指定的可视区域内。
    const { isIntersecting, target } = entrys[0]
    if (isIntersecting) {
      // 出现在可视区域内后,替换真实src,
      target.src = src
      // 停止对该元素的观察。
      observerRef.current.unobserve(itemRef.current)
    }
  }
  useLayoutEffect(() => {
    const options = {
      // 容器元素
      root: document.querySelector('#upload_container'),
      // 图片出现在容器元素可视区域上下100px内均视为图片出现
      rootMargin: '100px',
    }
    // 生成一个IntersectionObserver实例监听图片和容器的交叉状态。
    observerRef.current = new IntersectionObserver(cb, options)
    if (itemRef.current) {
      observerRef.current.observe(itemRef.current)
    }
  }, [])

这样就完美的实现的图片懒加载,告别计算dom的各种属性值~

参考文献