vue3 overflow 长列表虚拟滚动的知识记录

本文介绍了在uniapp中实现虚拟滚动的挑战,包括如何监听滚动事件和处理滚动条位置。作者发现使用`<view>`标签无法获取滚动属性,改用`<div>`后问题得到解决。此外,遇到页面切换后滚动条重置的问题,通过onHide和onShow结合定时器解决了该问题,但这种方法可能对性能有一定影响。文章提供了滚动事件处理和滚动位置控制的代码示例,并计划在后续文章中分享完整的虚拟滚动方案。

其实我是要在手机端实现虚拟列表(web端其实有很实用的插件可以用,没必要自己实现)。找了两天资料,也没有找到一个我满意的方案。最后看到一片比较通俗易懂的文章,按照他的方案实现了基本的虚拟列表。

PS:功能虽然实现了,但是还有小问题,比如快速滚动时效果一般。性能也还未测试。

uniapp下的坑

做虚拟列表,首先得能拿到滚动条的数据,也就是滚动监听事件。网上找了好久,基本就两个方案:

  1. 基于document什么的监听“scroll”事件。这个试了,确实可以哈。但是,如果我一个页面分两边,分别有两个滚动区域呢?这能监听到吗?存疑,我没有测试了
  2. 用vue提供(真的是vue提供的吗?官方文档翻遍都找到说明)的@scroll,来监听滚动事件。

当然,我选的第二个方案。于是,我就被折磨了很久。我的代码大概是这样写的:

    <view ref="scrollRef" style="overflow: auto" @scroll="scroll">
        <view v-for="item in 100" :key="item">
            <text>标题{{ item }}</text>
        </view>
    </view>

滚动确实没问题哈,滚动事件也是成功调用了,但是,里面没有网上查到的那些属性呀,什么scrollTopscrollHeight的,都找不到,明明他们也这么写的,为什么他们的有:
在这里插入图片描述
翻遍vue官网,uniapp官网,都找不到相关的说明。于是,我就怀疑是我用的<view>,他们用的<div>
,就只有这个的区别了。于是就尝试一下,果然,有了。

uniapp官网说用<view>代替<div>。也没说有这方面的区别呀。属实很坑。

PS:我现在用的H5,这样用没问题,但是编译成app和小程序不知道还行不行。

相关用法

首先,用@scroll监听了滚动事件后:滚动会触发:

    function scroll(event: any) {
        console.log(event);
        console.log(event.target.scrollTop); // 理解为滚动的距离,单位应该是px
   	}

其次呢,我上面还用了ref="scrollRef"绑定dom,这样就可以获得实例:

    const scrollRef = ref(null);
    function scroll(event: any) {
        console.log(event.target.scrollTop);
        console.log(scrollRef.value.scrollTop);
    }

上面两个属性能得到一样的数值。

scrollRef还能用来控制滚动轴的位置:

        scrollRef.value.scrollTop = 0; // 直接设置滚动条的位置
        
        // 也可使用scrollTo方法设置,支持设置滚动的动画。但是我没测试出效果
        scrollRef.value.scrollTo({
            top: 0,
            left: 0,
            behavior: "smooth",
        });

这个也是很实用的功能,没他很多功能就无法实现了。上面两种方法都可以。
可以看看这篇的介绍:https://blog.youkuaiyun.com/small_white_123/article/details/117792575

至于整体的虚拟滚动方案,等我实测没问题了,再写文章记录。

切换页面再切回来,导致滚动条自动回到起点的问题

这个问题在虚拟列表中尤为明显,因为会留空白。

在这里插入图片描述
我是先滚动了一段距离,然后点了购物车,再回到商品,看吧,两个滚动条都回到原点了。左侧因为没有使用虚拟滚动,还算正常点,只是回到起点而已,右侧直接留空。
因为这种自动重置滚动条不会触发滚动事件,我们的那些滚动参数还保持着原样,上面都是padding留空的,所以看过去就是一片空白。

我百度查了一圈,这个问题,在几年前就有人在讲了,可是到现在还是存在,难道这几年的技术进步仍然没有从根本上解决这问题吗?我有点不能理解,找不到答案。

当然,这个问题很多人会遇到,所以肯定有解决方案:

解决方案

首先,uniapp在切换页面时,会调用onHideonShow,这两个的作用如名,好理解,所以我理所当然的认为,在隐藏界面时,调用onHide记录当前滚动条的位置,然后回到页面时调用onShow重新设置滚动条位置即可解决。

然后实测并不是这么回事,onShow里面设置滚动条是不生效的。我是很不理解原因的。然后网上给的方案是,用定时器,在onShow结束后再设置滚动条,这样就可以了:

    // 下面是为了解决页面切换后滚动条回到起点的问题
    let scrollLeftTop = 0; // 记录左侧分类栏滚动条位置
    let scrollRightTop = 0; // 记录右侧商品栏滚动条位置
    // 在切换页面时会调用
    onHide(() => {
        scrollLeftTop = leftScroll.value.scrollTop;
        scrollRightTop = rightScroll.value.scrollTop;
    });
    // 再次页面显示时,会调用
    onShow(() => {
        // 还不能直接在这个onShow中对滚动条赋值,会不生效,得用定时器延迟一点执行才行
        var timer = setTimeout(() => {
            leftScroll.value.scrollTop = scrollLeftTop;
            rightScroll.value.scrollTop = scrollRightTop;
            clearTimeout(timer);
        }, 10);
    });

但是这种方案其实是有弊端的,是会触发页面重塑还是回流啥的。会对页面的性能造成影响,但目前也只找到这种方案是简单有效的。很无奈,以后要是有好方案再回来记录。

PS:我不知道在web端,会不会存在这个问题。移动端是这样的。

Vue3中,实现虚拟滚动列表可提升页面性能,让页面加载更快、更流畅。以下是实现及使用的相关信息: ### 实现原理 虚拟滚动的核心思想是在滚动时,只渲染可视区域内的列表项,而不是一次性渲染所有列表项。通过计算可视区域的起始索引和结束索引,动态渲染该范围内的列表项。当用户滚动时,根据滚动位置动态更新可视区域内的列表项,从而实现虚拟滚动的效果[^2]。 ### 实现步骤 1. **计算可视区域的起始和结束索引**:根据滚动位置、单个列表项高度和可视区域高度,计算出当前可视区域内的列表项的起始索引和结束索引。 2. **渲染可视区域内的列表项**:根据计算出的起始和结束索引,渲染该范围内的列表项。 3. **添加占位元素**:在可见列表项的上方和下方放置适当高度的占位元素,以模拟完整列表的滚动行为。这些元素的高度应该等于(总列表项数 - 可见列表项数)* 单个列表项高度[^3]。 ### 代码示例 以下是一个简单的Vue3虚拟滚动列表的实现示例: ```vue <template> <div class="virtual-list" @scroll="handleScroll"> <div class="placeholder-top" :style="{ height: topPlaceholderHeight + &#39;px&#39; }"></div> <div class="visible-items"> <div v-for="(item, index) in visibleItems" :key="index" class="list-item">{{ item }}</div> </div> <div class="placeholder-bottom" :style="{ height: bottomPlaceholderHeight + &#39;px&#39; }"></div> </div> </template> <script setup> import { ref, computed } from &#39;vue&#39;; // 模拟大量数据 const totalItems = 1000; const itemHeight = 30; const visibleHeight = 300; const scrollTop = ref(0); const listData = Array.from({ length: totalItems }, (_, index) => `Item ${index}`); // 计算起始索引 const startIndex = computed(() => Math.floor(scrollTop.value / itemHeight)); // 计算结束索引 const endIndex = computed(() => startIndex.value + Math.ceil(visibleHeight / itemHeight)); // 获取可视区域内的列表项 const visibleItems = computed(() => listData.slice(startIndex.value, endIndex.value)); // 计算上方占位元素的高度 const topPlaceholderHeight = computed(() => startIndex.value * itemHeight); // 计算下方占位元素的高度 const bottomPlaceholderHeight = computed(() => (totalItems - endIndex.value) * itemHeight); // 处理滚动事件 const handleScroll = (e) => { scrollTop.value = e.target.scrollTop; }; </script> <style scoped> .virtual-list { height: 300px; overflow-y: auto; position: relative; } .placeholder-top, .placeholder-bottom { position: absolute; left: 0; right: 0; } .placeholder-top { top: 0; } .placeholder-bottom { bottom: 0; } .list-item { height: 30px; line-height: 30px; border-bottom: 1px solid #ccc; } </style> ``` ### 使用教程 1. **创建Vue3项目**:使用Vue CLI或Vite创建一个新的Vue3项目。 2. **创建虚拟滚动组件**:将上述代码复制到一个新的Vue组件中,例如`VirtualList.vue`。 3. **在页面中使用组件**:在需要使用虚拟滚动列表的页面中引入并使用该组件。 ```vue <template> <div> <h1>Vue3 Virtual Scroll List</h1> <VirtualList /> </div> </template> <script setup> import VirtualList from &#39;./VirtualList.vue&#39;; </script> ``` ### 性能优化 - **防抖/节流**:对`scroll`事件处理函数使用防抖或节流技术,以减少事件触发的频率。 - **回收DOM**:考虑使用DOM回收技术,如将不可见的列表项缓存起来,而不是每次都销毁和重建。 - **避免内联样式**:使用CSS类而不是内联样式来设置列表项和占位元素的样式[^3]。 ### 测试和调整 实现虚拟滚动列表后,需要进行测试和调整,确保滚动效果流畅,性能得到提升。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lsjweiyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值