长列表渲染-懒加载-虚拟列表

以下案例模拟10万条数据

一次性渲染

LCP 1.68s

 <script>

        // 获取元素

        let ul= document.querySelector('.bottom ul')

        // 渲染10万个li

        for(let i=0;i<100000;i++){

            let li = document.createElement('li')

            li.innerText = i

            ul.appendChild(li)

        }

  

    </script>

分页

用户只能看到当前页的数据,待用户翻页时,再动态加载下一页的数据。这种方法可以极大减少一次性渲染的数据量。

懒加载

LCP 0.09s

每次只渲染一部分(比如10条),等剩余部分滚到可见区域,再渲染一部分。有点像分页

  • clientHeight:视口的高度;
  • scrollTop:元素内容被卷去的头部高度值;
  • scrollHeight: 元素内容总高度包括不可见元素

缺点
当滚动到数据底部,页面仍然存在大量的dom
节点

 <script>

        // 获取内容列表

        let ul = document.querySelector('.bottom ul')

        // 获取容器元素

        let bottom = document.querySelector('.bottom')

        // 已经加载的项目

        let count = 0;

        // 每次加载的项目

        let loadCount = 7;

        // 总共的项目

        let sum = 100000;

  

        // 懒加载

        function lazyLoad() {

            // 判断是否加载完成

            if (count + loadCount >= sum) {

                // 如果剩余项目数少于loadCount,则只加载剩余的项目

                let remainingItems = sum - count;

                for (let i = 0; i < remainingItems; i++) {

                    let li = document.createElement('li');

                    li.innerHTML = count + 1 + i;

                    ul.appendChild(li);

                    count++;

                }

                console.log("加载完成");

                return;

            }

  

            // 每次加载的项目

            for (let i = 0; i < loadCount; i++) {

                let li = document.createElement('li');

                li.innerHTML = count + 1;

                count++;

                ul.appendChild(li);

            }

  

        }

  

        // 设置节流函数

        function throttle(fn, delay) {

            let timer = null;

            return () => {

                if (timer) {

                    return;

                };

                timer = setTimeout(() => {

                    fn();

                    timer = null;

                }, delay)

            }

        }

  
  

        // 监听滚动事件

        bottom.addEventListener('scroll', throttle(() => {

            console.log("scroll触发");

  

            if (bottom.scrollTop + bottom.clientHeight >= ul.scrollHeight - 1) {

                lazyLoad();

            }

        }, 500))

  

        // 初始化,加载页面数据

        lazyLoad();

  
  
  
  

    </script>

虚拟列表

LCP 0.11s

核心思想是只渲染用户可见区域的元素,减少浏览器的渲染开销, 解决懒加载下渲染的dom节点过多的问题

  <script>

        // 获取容器和列表元素

        const listScroll = document.querySelector('.bottom')

        const list = document.querySelector('.list')

  

        // 源数据

        const dataSource = []

        // 渲染数据=> 通过定义首位index截取源数据

        let renderData = []

  

        // item的高度

        const itemHeight = 100

        // listScroll容器能够显示的最大数量

        //可视区容器最大显示的元素个数 向上取整,保证可视区域占满

        const maxCount = Math.ceil(listScroll.clientHeight / itemHeight) + 4

        // 开始位置索引

        let startIndex = 0

        // 记录到的位置索引

        let pointerIndex = 0

  

        // 结束位置索引

        let endIndex = maxCount

  

        // 源数据获取

        function GetData() {

            for (let i = 0; i < 100000; i++) {

                dataSource.push(i)

            }

        }

  

        // 计算结束位置索引

        function ComputePointerPosition() {

  

            const end = startIndex + maxCount

            // 注意判断是否超出数据源长度

            endIndex = dataSource[end] ? end : dataSource.length

        }

        // 每一次的渲染数据

        function GetRenderData() {

            //区间左闭右开

            renderData = dataSource.slice(startIndex, endIndex)

        }

  
  

        // 渲染

        function Render() {

            // 计算开始与结束位置

            ComputePointerPosition(startIndex, endIndex)

            // 获得渲染数据

            GetRenderData()

            // 将截取的渲染数据生成动态的list元素,填充到list内容元素

            list.innerHTML = renderData.map(item => `<li>${item}</li>`).join('')

        }

  
  

        // 监听滚动事件

        listScroll.addEventListener('scroll', ScrollHandle)

  

        // 监听listOut滚动事件

        function ScrollHandle() {

            // 更新开始位置索引:滚动的距离 / 每个元素的高度

            startIndex = Math.floor(listScroll.scrollTop / itemHeight)

            // 一致不做渲染

            if (pointerIndex === startIndex) return

            // 更新位置,重新渲染

            pointerIndex = startIndex

            // 更新位置,重新渲染

            Render()

            // 测试发现每次向下滚动一个元素,列表会向上移动一个元素的位置,所以增加transform属性,使列表位置向下移动一个元素的位置

            // startIndex表示已经上移到的元素的个数,itemHeight表示每个元素的高度

            list.style.transform = `translateY(${startIndex * itemHeight}px)`

  

        }

  

        //获取源数据

        GetData()

        Render()

  
  
  
  
  
  
  

    </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值