大数据列表优化

1,088 阅读2分钟

对于数量大的列表中,如果一下子要全部渲染,都会出现渲染慢的问题,导致整个页面卡顿。因此针对这种情况,列举下常见方案。

1、分页加载

传统大数据列表处理方式,通过分页调用一部分数据,减少大量数据渲染

2、懒加载(滚动加载)

原理:通过监听滚动事件和回调函数的机制,配合使用元素相关属性和方法scrollTop、getClientHeight(container)、scrollHeight实现了滚动加载数据的功能。之前在赛事项目中就是单纯使用这种技术加载列表数据

简单案例:

//InfiniteScrollUtil.js
const throttle = (fn, wait) => {
    let inThrottle, lastFn, lastTime;
    return function () {
      const context = this;
      const args = arguments;
      if (!inThrottle) {
        fn.apply(context, args);
        lastTime = Date.now();
        inThrottle = true;
      } else {
        clearTimeout(lastFn);
        lastFn = setTimeout(function () {
          if (Date.now() - lastTime >= wait) {
            fn.apply(context, args);
            lastTime = Date.now();
          }
        }, Math.max(wait - (Date.now() - lastTime), 0));
      }
    };
  };
  const getPositionSize = (el, prop) => {
    return el === window || el === document
      ? document.documentElement[prop]
      : el[prop];
  };
  
  const getClientHeight = (el) => {
    return getPositionSize(el, "clientHeight");
  };
  
  const isFunction = (functionToCheck) => {
    const getType = {};
    return (
      functionToCheck &&
      getType.toString.call(functionToCheck) === "[object AsyncFunction]"
    );
  };
  export const handleScroll = function (container, cb, options) {
    const { distance } = options;
    const containerInfo = container.getBoundingClientRect();
    if (!containerInfo.width && !containerInfo.height) return;
  
    let shouldTrigger = false;
  
    const scrollBottom = container.scrollTop + getClientHeight(container);
    shouldTrigger = container.scrollHeight - scrollBottom <= distance;
  
    if (shouldTrigger && isFunction(cb)) {
      cb.call(container, container, options);
    }
  };
  
  export const onScroll = (container, options) => {
    return throttle(
      handleScroll.bind(container, container, options.cbFunc, options),
      options.delay
    );
  };
  
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
    </style>
    <script  type="module">
    import {  onScroll } from "./InfiniteScrollUtil.js";
    const createLiEle=(container)=>{
        const fragment = document.createDocumentFragment();
            new Array(10).fill(1).forEach(
            (m,i)=>{
                const tempEle = document.createElement("li");
                tempEle.innerHTML =`${document.querySelectorAll("li").length+i+1}数据`
                fragment.append(tempEle);
                }
            )
            container.append(fragment);
    }
    window.onload=()=>{
        const container = document.querySelector("ul");
        const options = {
        delay: 200,
        distance: 10,
        cbFunc: async (container, options) => {
            createLiEle(container);
        }
        };
        if (container) {
            const onScrollFunc = onScroll(container, options);
            container.addEventListener("scroll", onScrollFunc);
        }
        createLiEle(container);
    }

    </script>
</head>
<body>
    <ul style="height: 100px;overflow-y: scroll;">
    </ul>
</body>
</html>

3、虚拟列表

原理:它通过只渲染可见区域内的数据项(根据可见区域的大小和滚动位置,动态计算需要渲染的数据项,同时设置padding,并在滚动时进行相应的更新),而不是渲染整个列表,从而提高了性能和响应速度。

image.png

简单案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #scroller{
            height: 100px;overflow-y: scroll;
        }
        #container{
            height: 100px;
        }
        .item{
            height: 20px;
            text-align: center;
        }
    </style>

</head>
<body>
    <div id="scroller">
        <div id="container">

        </div>
    </div>
</body>
</html>
<script>
    const scroller=document.querySelector("#scroller")
    const container=document.querySelector("#container")
    const sourceData=new Array(100000).fill(1).map((m,i)=>i);
    const itemHeight=20;
    const containerHeight=container.clientHeight;
    let [startIndex,ednIndex,paddingTop,paddingBottom]=[0,9,0,sourceData.length*itemHeight-100]
    
    const createLiStr=(startIndex,ednIndex)=>{
        const data=sourceData.slice(startIndex,ednIndex+1);
        return data.map(m=>`<div class="item">${m}</div>`).join("");
    }
    const setPadding=(paddingTop,paddingBottom)=>{
        container.setAttribute("style",`padding-top:${paddingTop}px;padding-bottom:${paddingBottom}px`)
    }
    scroller.addEventListener("scroll",()=>{
        startIndex=Math.floor(scroller.scrollTop/itemHeight);
        ednIndex=Math.floor((scroller.scrollTop+containerHeight)/itemHeight);
        container.innerHTML= createLiStr(startIndex,ednIndex);
        paddingTop=itemHeight*startIndex;
        paddingBottom=sourceData.length*itemHeight - 100 -paddingTop
        setPadding(paddingTop,paddingBottom)
    })
    container.innerHTML= createLiStr(startIndex,ednIndex);
    setPadding(paddingTop,paddingBottom)

</script>

90a03348-531a-4e8b-b049-56e4bd3f0de4.gif

常见框架中使用虚拟技术的插件

React 相关插件和库:

  1. react-virtualized:React Virtualized 是一个用于构建高性能虚拟列表的 React 组件库。它提供了一系列的组件,如 VirtualList、InfiniteLoader、AutoSizer 等,用于实现可滚动的、大量数据的列表。React Virtualized 提供了丰富的配置选项和 API,使开发者能够高度自定义虚拟列表的行为。
  2. react-window:React Window 是另一个用于构建高效虚拟列表的 React 组件库。它提供了一系列的组件,如 FixedSizeList、VariableSizeList、FixedSizeGrid 等,可以根据不同的需求选择适当的组件。React Window 的组件具有高性能和低内存占用的特点,适用于大数据集的列表展示。

Vue 相关插件和库:

  1. vue-virtual-scroller:Vue Virtual Scroller 是一个用于 Vue.js 的虚拟列表组件库。它提供了一系列的组件,如 VirtualScroller、DynamicScroller 等,用于实现高性能的虚拟滚动列表。Vue Virtual Scroller 提供了可配置的选项和事件钩子,方便自定义和扩展。
  2. vue-virtual-scroll-list:Vue Virtual Scroll List 是另一个用于 Vue.js 的虚拟列表插件。它基于虚拟滚动技术,只渲染可见区域内的数据项,从而提高性能和响应速度。Vue Virtual Scroll List 提供了简单易用的 API,支持动态高度、滚动到指定位置、无限加载等功能。

参考

虚拟列表