学会前端虚拟滚动,看这篇就够了

念念不忘 必有回响 2024-07-24 13:33:03 阅读 53

一. 虚拟滚动是什么?

前端虚拟滚动是一种用于优化长列表或大量数据展示的技术。它的主要原理是只渲染用户当前可见区域的数据,而不是一次性渲染整个列表或数据集。

在传统的列表渲染中,如果数据量很大,渲染所有数据可能会导致性能下降,特别是在滚动时。虚拟滚动通过动态计算可见区域的位置和需要渲染的数据范围,只渲染可见区域内的部分数据,并在滚动时实时更新渲染的内容。

总结下就是:

接口返回了大量数据,前端将大量数据保存起来;虚拟滚动的策略是:因为无论你返回了多大的数据量,用户可以看到的永远只是可视区域展示的数据,只是大量数据的很少一部分,所以保证可视区域的数据正常渲染就行。

二. 虚拟滚动怎么实现?

先上代码直观体验下

index.vue

<code><template>

<div class="container">code>

<my-virtual-scroller :items="list" />code>

</div>

</template>

<script>

import myVirtualScroller from './myVirtualScroller.vue'

// 模拟一个长列表

const list = []

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

list.push({

id: i,

label: `virtual-list ${ i}`

})

}

export default {

components: {

myVirtualScroller

},

data() {

return {

list: list

}

}

}

</script>

<style scoped>

.container {

height: 300px;

border: 1px solid #ccc;

}

</style>

myVirtualScroller.vue

// 传入数据可以实现虚拟滚动事件

// 这是一个封装好的子组件 传入数组进行渲染

<template>

<!-- 最底层的可视区容器 -->

<div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">code>

<!-- 中间的可滚动区域,z-index=-1,高度和真实列表相同,目的是出现相同的滚动条 -->

<div

class="infinite-list-phantom"code>

:style="{ height: listHeight + 'px' }"code>

></div>

<!-- 最上层的可视区列表,数据和偏移距离随着滚动距离的变化而变化 -->

<div class="infinite-list" :style="{ transform: getTransform }">code>

<div

class="infinite-list-item"code>

v-for="item in visibleData"code>

:key="item.id"code>

:style="{ height: itemSize + 'px' }"code>

>

{ { item.label }}

</div>

</div>

</div>

</template>

<script>

export default {

name: "MyVirtualList",

props: {

//父组件传过来的数据

items: {

type: Array,

default: () => [],

},

//可视区域每一项的高度 没有传参的话默认高度是100px

itemSize: {

type: Number,

//没有高度的话 高度会默认是100px

default: 100,

},

},

computed: {

//列表总高度

listHeight() {

// 计算当前传值数据的总长度 * 每个盒子的高度 计算出所有数据的整体高度

return this.items.length * this.itemSize;

},

//可视区列表的项数

visibleCount() {

return Math.ceil(this.screenHeight / this.itemSize);

},

//可视区列表偏移距离对应的样式

getTransform() {

return `translate3d(0,${ this.startOffset}px,0)`;

},

//获取可视区列表数据

visibleData() {

// 截取出可视区域显示的盒子 进行渲染

return this.items.slice(

this.start,

Math.min(this.end, this.items.length)

);

},

},

mounted() {

// 获取可是区域的高度

this.screenHeight = this.$refs.list.clientHeight;

// 开始

this.start = 0;

// 结束

this.end = this.start + this.visibleCount;

},

data() {

return {

screenHeight: 0, //可视区域高度

startOffset: 0, //偏移距离

start: 0, //起始索引

end: 0, //结束索引

};

},

methods: {

scrollEvent() {

//当前滚动条滚动位置

let scrollTop = this.$refs.list.scrollTop;

//此时的开始索引 使用当前滚动条高度 / 盒子高度 拿到的是当前是哪一个盒子为第一个

this.start = Math.floor(scrollTop / this.itemSize);

//此时的结束索引

//获取到当前第一个盒子的数值 加 计算好可视区域可容纳多少盒子的值

this.end = this.start + this.visibleCount;

//此时的偏移距离

this.startOffset = scrollTop - (scrollTop % this.itemSize);

},

},

};

</script>

<style scoped>

.infinite-list-container {

height: 100%;

overflow: auto;

position: relative;

}

.infinite-list-phantom {

position: absolute;

left: 0;

top: 0;

right: 0;

}

.infinite-list {

left: 0;

right: 0;

top: 0;

position: absolute;

}

.infinite-list-item {

line-height: 50px;

text-align: center;

color: #555;

border: 1px solid #ccc;

box-sizing: border-box;

}

</style>

虚拟滚动的思路总结如下

1. 确定视窗位置:通过获取列表滚动区域的scrollTop属性,确定当前视窗的位置;

2. 计算需要渲染的节点:根据视窗位置和子节点的高度,计算出需要真实渲染的节点范围。可以使用Math.floor(scrollTop/itemHeight)和Math.ceil(viewHeight/itemHeight)来确定起始索引和渲染的节点数量;

3. 移动渲染的节点到视窗内:将需要渲染的节点移动到视窗内,可以通过修改节点的位置或使用其他方式实现;

4. 按需加载数据:根据滚动位置,动态加载相应的数据,只加载当前视窗可见范围内的数据,减少数据量和渲染负担;

5. 处理滚动事件:监听滚动事件,根据滚动位置实时更新需要渲染的节点和数据。

三. 虚拟滚动示例代码难点解析

1. 可视化容器为什么要包裹两个盒子,并且设置绝对定位?

第一个盒子(类名为"infinite-list-phantom")的作用:

其总高度为内容的总高度,主要要为了撑起父盒子,使父盒子有滚动条。

第二个盒子(类名为‘infinite-list’)的作用:

这个要可视区域,是用户所能看到的界面,也是数据渲染的界面。

至于为什么要设置绝对定位,是因为需要第二个盒子覆盖第一个盒子,以满足第二个盒子展现在可视界面。

2. 为什么要设置:style=“{ transform: getTransform }”?

原因如下:

因为可视界面区域有限,在滚动后,会展示总的内容区域和可视区域不相交的部分,在可视区域就会出现部分或全部空白。滚动的过程本质要内容区域的位置变化,其变化的数值可以通过scrollTop来查看;通过transform的作用,抵消scrollTop的位置变动,使得我们看到的还是原先的可视区域位置的dom节点,只不过在滚动的过程中,渲染数据会改变。

3.为什么要设置偏移距离?

相关代码如下:

//此时的偏移距离

this.startOffset = scrollTop - (scrollTop % this.itemSize);

原因如下:

为了使得滚动的时候有动态效果,体验会更好;可以尝试将scrollTop - 改成 scrollTop + 测试,就可以更好的去理解2、3点疑问。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。