阅读 817

[开源推荐]:一个轻量的React懒加载组件,冰冰用了都说6~

前言

现如今前端React生态圈已有不少优秀的懒加载组件,比如react-lazy-loadreact-lazy-mount等,但是它们并没有实现一个组件完全的懒加载,当DOM没有出现在视口中时,它们会先渲染出一部分,等到出现在视口中才会渲染要懒加载的目标,大多数基本都是图片懒加载,虽然已经满足了绝大多数要求,但是过程并不是很完美。而真正实现组件懒加载的开源库,还是通过scroll事件监听出现在窗口中的DOM,性能并不是很优秀,笔者所开源的这个组件既不用使用scroll事件,也能实现组件懒加载。

IntersectionObserver

这个组件所依赖的API是IntersectionObserver,关于这个API掘金已有不少文章来描述讲解了,我这里再简单描述一下:监听一个DOM元素,若是这个DOM元素出现在你规定的视口中(一般都为window窗口),调用指定的回调函数。但是我已看到的所有文章只是使用原生JS的方法来讲解怎么使用。
比如:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
    <script>
      const callback = (entries) => {
        entries.forEach((item) => {
          const isIntersecting = item.isIntersecting;
          if (isIntersecting) {
            const image = new Image();
            image.src = "http://xxxx";
            item.target.appendChild(image);
          }
        });
      };
      const io = new IntersectionObserver(callback);
      const container = document.querySelector(".container");
      [...container.children].forEach((ele) => {
        ele.observe(ele);
      });
    </script>
  </body>
</html>
复制代码

上面代码是我大概写的一个使用教程,中只要container中一个子元素出现在视口中,就加载出一张图片。但是这种写法以及思路用在React或者Vue中使用是不符合规范的。那么我们该如何在React中使用呢?

组件思路

在React中拿到真实DOM

众所周知,React中是虚拟DOM,也就是一个对象,React内部用这个虚拟DOM渲染成真实的DOM,我们在React组件内部如何不是操作DOM的话,一般是不会用到真实DOM的,而IntersectionObserver必须监听真实的DOM才可以,那怎么办呢?
这时候Ref就出场了,Ref挂到一个ReactDOM上可以帮助我们拿到当前DOM的所有信息,包括真实DOM。

  const Example = () => {
    const domRef  = useRef();
    return( 
      <div ref={domRef}>
        { list.map(item=>(<div>{item}</div>)) }
      </div>
    )
  }
复制代码

利用这个特性我们就可以拿到div的所有信息,包括它的子元素,这样我们就可以通过IntersectionObserver来监听它所有的子元素是否出现在视口中。 那么问题来了,监听的元素都是已经被渲染出来的元素,那么还怎么懒加载呢?接下来这个思路就是step by step

step by step

我们先来看两个组件:

const LazyList = (props) => {
  const [renderCont,setRenderCount] = useState(2)
  const children = props.children;
  const renderList = useMemo(()=>{
    return children.slice(0,renderCont)
  },[children,renderCont])
  useEffect(()=>{
  //...
  },[renderCont])
  return(
   <div>{renderList}</div>
  )
}

const Example = () => {
  return(
  	<LazyList>
      {list.map(item=>{
        <div>111</div>
      })}
    </LazyList>
  )
}
复制代码

LazyList这个组件中,渲染children中的renderCont个,renderList一变化,在useEffect中的回调函数中通过ref拿到真实DOM,进行监听。

提前渲染第renderCont + 1个DOM

上边我们能够先渲染renderCont个DOM,但是如何进行懒加载呢?就是当renderCont中最后一个出现在视图中时,渲染第renderCont + 1个DOM,setRenderCount(renderCont + 1),这里是在IntersectionObserver回调函数中进行。循环往复,就实现了组件懒加载。

效果


注意看控制台的DOM区域,初始渲染出两个,每次下拉渲染出来的两个出线在试图中,后续两个DOM就渲染出来了,视觉效果上也不会出现中断的感觉。

最后

图示是我个人项目用来演示的,这个组件刚刚开源,目前我司我们部门3个项目已经在生产环境使用了,没有任何问题。并且功能只为实现长列表懒加载,对于需要一次性渲染大量数据但是不需要完全展示的业务比较契合,还未暴露接口指示渲染到第几个DOM,所以有一个功能并不支持:DOM渲染的回调函数,也就是暴露出渲染到了第几个DOM,目前正在完善这个功能,下个版本会上线。
此组件在web端和移动端都支持,并且引入了IntersectionObserverpolyfill,包的总体积为4.5KB。 具体代码以及逻辑看github吧~
github地址:点击这里
安装:

 npm i lazylist-react
 // or
 yarn add lazylist-react
复制代码

任何疑问都可以在githubissus留言或者在文章底部评论,我都会看并且回复。
希望大家多多支持呀😃

文章分类
前端
文章标签