当前位置:   article > 正文

vue之大量数据时实现虚拟上下滚动元素无跳动_avue虚拟滚动

avue虚拟滚动

虚拟滚动的实现

等不及啦,跳转到全部代码

前提

虚拟滚动是一种动态渲染页面元素的技术。当页面需要展示大量数据时(例如一万条),不能一次性将所有数据渲染到页面上,否则会导致性能问题。因此,需要采用虚拟滚动技术,假装展示了全部数据,实际上只渲染了一部分。
1688235609787.png

步骤

首先,我们的网页的 HTML 结构如下:

<div class="appbox" ref="appBox" @scroll="appBoxScroll($event)">
    <div class="appbox-scroll" :style="{ height: containerHeight + 'px' }" style="position: relative;">
        <div :style="{ transform: `translateY(${appBoxOffset}px)` }" style="position: absolute; width: 100%;">
            <div class="boxcontent" v-for="(item, key) in showItem" :key="key">
                {{ item }}
            </div>
        </div>
    </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对应的 CSS 样式如下:

.appbox {
    position: relative;
    width: 200px;
    height: 200px;
    overflow: auto;
    border: 1px solid #ccc;
}

.boxcontent {
    height: 40px;
    line-height: 40px;
    border: 1px solid red;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

逐步解释

  • <div class="appbox" ref="appBox" @scroll="appBoxScroll($event)">:一个具有 CSS 类名为 “appbox” 的 <div> 元素。通过 ref 属性设置了一个引用名为 “appBox”,可以在 Vue 组件中通过 this.$refs.appBox 来引用该元素。通过 @scroll 事件绑定了一个滚动事件,当滚动事件触发时,会调用 Vue 组件中的 appBoxScroll 方法,并将事件对象 $event 作为参数传递给该方法。

  • <div class="appbox-scroll" :style="{ height: containerHeight + 'px' }" style="position: relative;">:一个具有 CSS 类名为 “appbox-scroll” 的 <div> 元素。通过动态绑定的方式设置了该元素的高度,绑定的值为 containerHeight 加上 ‘px’ 单位。containerHeight 是一个在 Vue 组件中定义的响应式数据,其值会随着数据的变化而更新。

  • <div :style="{ transform: translateY(${appBoxOffset}px) }" style="position: absolute; width: 100%;">:该 <div> 元素的样式部分包含了两个部分:

    • :style="{ transform: translateY(${appBoxOffset}px) }":使用动态绑定的方式设置该 <div> 元素的变换样式,通过 appBoxOffset 的值来设置 translateY 的偏移量。appBoxOffset 是一个在 Vue 组件中定义的响应式数据,其值会随着数据的变化而更新。

    • style="position: absolute; width: 100%;":设置该 <div> 元素的绝对定位和宽度为 100%。

  • <div class="boxcontent" v-for="(item, key) in showItem" :key="key">:一个具有 CSS 类名为 “boxcontent” 的 <div> 元素,根据 v-for 指令生成多个。v-for 指令根据 showItem 数组的内容循环生成多个 <div class="boxcontent"> 元素。showItem 是一个在 Vue 组件中定义的计算属性,根据条件切片处理数据后返回一个新的数组。

  • {{ item }}:在每个 <div> 元素中显示当前循环项 item 的内容。

JavaScript 部分

通过使用 ref,我们可以创建具有响应性的数据引用。使用 computed,我们可以创建根据其他响应式数据自动计算的属性。而 nextTick 则用于在 DOM 更新后执行异步操作。

import { ref, computed, nextTick } from "vue";
  • 1

下面的代码使用 computed 函数创建了一个计算属性 showItem。计算属性是根据其他响应式数据自动计算的属性。

const showItem = computed(() => {
    return [...arr.value.slice(currentIndex.value, showItemNum.value + currentIndex.value + 1)];
});
  • 1
  • 2
  • 3

在这里,showItem 的计算函数使用了三个响应式数据:arr.valuecurrentIndex.valueshowItemNum.value。它从 arr.value 数组中提取了一部分元素,这部分元素的起始索引是 currentIndex.value,要提取的元素个数是 showItemNum.value。最后,计算函数返回提取出的元素作为 showItem 的值。这样,每当 arr.valuecurrentIndex.valueshowItemNum.value 发生变化时,showItem 的值会自动重新计算。

接下来是一些初始变量的定义:

// 设置1W条模拟数据
const count = ref(10000);
let arr = ref([]);
for (let index = 0; index < count.value; index++) {
    arr.value.push(index);
}

// 容器真实高度,给增加滚动条,假装渲染数据多
let containerHeight = ref(arr.value.length * 40);
// 当前状态索引
let currentIndex = ref(0);
// 应该显示的 DOM 数量
let showItemNum = ref(0);
// 容器 DOM 节点
const appBox = ref(null);
// 容器视窗高度
let appBoxHeight = ref(0);
// 容器内部元素 DOM 节点下降偏移
let appBoxOffset = ref(0);
// 老高度
let oldOffset = 0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在回调函数中,首先通过 appBox.value.clientHeight 获取了容器的高度,并将其赋值给 appBoxHeight.value。接下来,通过运算 Math.ceil(appBoxHeight.value / 40),计算出应该显示的 DOM 元素的数量,并将结果赋值给 showItemNum.value。这里每个 DOM 元素的高度是 40 像素。通过将这两个值赋给响应式数据 appBoxHeight.valueshowItemNum.value,可以使它们成为计算属性的依赖项,从而触发计算属性的重新计算。

nextTick(() => {
    // 获取容器高度
    appBoxHeight.value = appBox.value.clientHeight;
    // 运算出应该显示的 DOM 数量
    showItemNum.value = Math.ceil(appBoxHeight.value / 40);
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

最后是关键的方法函数代码,其中有注释帮助理解:

const appBoxScroll = (e) => {
    let tempNum = 0;
    let scrollTop = e.target.scrollTop;
    // 判断向下还是向上滑动
    if (scrollTop < oldOffset) {
        // 计算当前状态的索引
        tempNum = Math.floor(scrollTop / 40);
        // 当前状态的索引发生变化才触发视图层刷新
        if (tempNum !== currentIndex.value && scrollTop > 40) {
            currentIndex.value = tempNum;
            appBoxOffset.value = scrollTop - 40;
        } else if (scrollTop <= 40) {
            appBoxOffset.value = 0;
            currentIndex.value = 0;
        }
    } else {
        // 计算当前状态的索引
        tempNum = Math.floor(e.target.scrollTop / 40);
        // 当前状态的索引发生变化才触发视图层刷新
        if (tempNum !== currentIndex.value) {
            currentIndex.value = tempNum;
            appBoxOffset.value = scrollTop;
        }
    }
    // 记录老高度
    oldOffset = scrollTop;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

以上就是实现虚拟滚动的代码。在 Vue 组件中,可以将 HTML、CSS 和 JavaScript 部分整合在一起使用。通过这些代码,可以实现虚拟滚动效果,优化大量数据的渲染性能。
效果:
录屏网页测试.gif

全部代码

<template>
    <div class="appbox" ref="appBox" @scroll="appBoxScroll($event)">
        <div class="appbox-scroll" :style="{ height: containerHeight + 'px' }" style="position: relative;">
            <div :style="{ transform: `translateY(${appBoxOffset}px)` }" style="position: absolute; width: 100%;">
                <div class="boxcontent" v-for="(item, key) in showItem" :key="key">
                    {{ item }}
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import { ref, computed, nextTick } from "vue";
const showItem = computed(() => {
    return [...arr.value.slice(currentIndex.value, showItemNum.value + currentIndex.value + 1)];
});
//设置1W条模拟数据
const count = ref(10000);
let arr = ref([]);
for (let index = 0; index < count.value; index++) {
    arr.value.push(index);
}
//容器真实高度,给增加滚动条,假装渲染数据多
let containerHeight = ref(arr.value.length * 40);
// 当前状态索引
let currentIndex = ref(0);
// 应该显示的 DOM 数量
let showItemNum = ref(0);
// 容器dom节点
const appBox = ref(null);
// 容器视窗高度
let appBoxHeight = ref(0);
// 容器内部元素dom节点下降偏移
let appBoxOffset = ref(0);
// 老高度
let oldOffset = 0;
nextTick(() => {
    //获取容器高度
    appBoxHeight.value = appBox.value.clientHeight;
    //运算出应该显示的 DOM 数量
    showItemNum.value = Math.ceil(appBoxHeight.value / 40);
});
const appBoxScroll = (e) => {
    let tempNum = 0;
    let scrollTop = e.target.scrollTop;
    // 判断向下还是向上滑动
    if (scrollTop < oldOffset) {
        //计算当前状态的索引
        tempNum = Math.floor(scrollTop / 40);
        //当前状态的索引发生变化才触发视图层刷新
        if (tempNum !== currentIndex.value && scrollTop > 40) {
            currentIndex.value = tempNum;
            appBoxOffset.value = scrollTop - 40;
        } else if (scrollTop <= 40) {
            appBoxOffset.value = 0;
            currentIndex.value = 0;
        }
    } else {
        //计算当前状态的索引
        tempNum = Math.floor(e.target.scrollTop / 40);
        //当前状态的索引发生变化才触发视图层刷新
        if (tempNum !== currentIndex.value) {
            currentIndex.value = tempNum
            appBoxOffset.value = scrollTop;
        }
    }
    // 记录老高度
    oldOffset = scrollTop;
};
</script>
<style>
.appbox {
    position: relative;
    width: 200px;
    height: 200px;
    overflow: auto;
    border: 1px solid #ccc;
}

.boxcontent {
    height: 40px;
    line-height: 40px;
    border: 1px solid red;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/193866
推荐阅读
相关标签
  

闽ICP备14008679号