Что такое виртуальный список
Виртуальный список — это список списков可视区域
оказывать, чтобы非可见区域
Техника, которая значительно повышает производительность рендеринга за счет отсутствия рендеринга или частичного рендеринга.
Зачем использовать виртуальный список
Иногда мы сталкиваемся с некоторыми бизнес-сценариями, список для отображения очень длинный, и метод подкачки не может быть использован.Если все данные отображаются на странице одновременно, браузер сильно застревает, потому что рендерингdom
Это занимает много времени.虚拟列表
Это метод оптимизации для длинных списков, который значительно повышает производительность рендеринга за счет рендеринга только данных видимой области.
Как использовать виртуальные списки
В настоящее время существует множество известных библиотек для виртуальных списков, таких какvue-virtual-scroller,vue-virtual-scroll-list,react-virtualizedПодождите, позвольте мне представить вам нижеvue-virtual-scroller
Как использовать эту прекрасную библиотеку, а затем принять вас для реализации краткой версии виртуального списка. Вы готовы, поехали!
Установить
npm install --save vue-virtual-scroller
Компонент RecycleScroller
Применимо к случаю, когда высота каждого элемента в списке определена, высота может быть установлена одинаковой или высота каждого элемента может быть настроена отдельно.
src/components/virtualRecycleScroller.vue
<template>
<RecycleScroller
class="scroller"
:items="items"
:item-size="50"
key-field="id"
v-slot="{ item }"
>
<div class="desc">
{{ item.label }}
</div>
</RecycleScroller>
<!-- items: 需要渲染的列表,itemSize: 列表项的高度,keyField: 列表循环的key值 -->
</template>
<script>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
export default {
components: {
RecycleScroller
},
props: {
items: Array
}
}
</script>
<style scoped>
.scroller {
height: 100%;
}
.desc {
height: 50px;
line-height: 50px;
text-align: center;
box-sizing: border-box;
border: 1px solid #ccc;
}
</style>
src/App.vue
<template>
<div class="container">
<virtual-recycle-scroller :items="list" />
</div>
</template>
<script>
import virtualRecycleScroller from '@/components/virtualRecycleScroller'
// 模拟一个长列表
const list = []
for(let i=0; i<10000; i++) {
list.push({
id: i,
label: `virtual-list ${i}`
})
}
export default {
components: {
virtualRecycleScroller
},
data() {
return {
list: list
}
}
}
</script>
<style scoped>
.container {
height: 300px;
border: 1px solid #ccc;
}
</style>
Эффект следующий:
Список из 10 000 фрагментов данных отображается мгновенно, а прокрутка чрезвычайно плавная, разве это не приятно!
Компонент DynamicScroller
Полезно, когда каждый элемент в списке очень неопределенный.
src/components/virtualDynamicScroller.vue
<template>
<DynamicScroller class="scroller" :items="items" :min-item-size="50">
<template v-slot="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[item.label]"
:data-index="index"
>
<div class="desc">{{ item.label }}</div>
</DynamicScrollerItem>
</template>
</DynamicScroller>
<!-- minItemSize: 列表项初次渲染使用的最小高度-->
<!-- active: 保持视图,防止不必要的重新计算 -->
<!-- sizeDependencies: 影响高度的值,如果发生变化,则重新计算 -->
</template>
<script>
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
export default {
components: {
DynamicScroller,
DynamicScrollerItem
},
props: {
items: Array
}
}
</script>
<style scoped>
.scroller {
height: 100%;
}
.desc {
padding: 12px;
text-align: center;
border: 1px solid #ccc;
}
</style>
src/App.vue
<template>
<div class="container">
<virtual-dynamic-scroller :items="list" />
</div>
</template>
<script>
import virtualDynamicScroller from '@/components/virtualDynamicScroller.vue'
// 模拟一个长列表
const list = []
for(let i=0; i<10000; i++) {
list.push({
id: i,
label: `virtual-scroller ${i}`
})
}
// 模拟一个内容不同的列表项
list[2].label = `virtual-scroller 2 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞
前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞
前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 前端阿飞 `
export default {
components: {
virtualDynamicScroller
},
data() {
return {
list: list
}
}
}
</script>
<style scoped>
.container {
height: 300px;
border: 1px solid #ccc;
}
</style>
Эффект следующий:
Вы можете видеть, что высота элемента списка меняется с изменением содержимого, и он по-прежнему очень шелковистый!
принцип виртуального списка
Когда вы впервые слышите термин «виртуальный список», он кажется вам очень высоким, но на самом деле, разобравшись с его принципом, вы обнаружите, что он очень прост. Без лишних слов, начнем с картинки:
-
Контейнер видимой области: его можно рассматривать как коробку внизу, содержащую все элементы.
-
Прокручиваемая область: ее можно рассматривать как средний слой.Предположим, что имеется 10 000 единиц данных, а высота каждого элемента списка равна 50, тогда высота прокручиваемой области составляет 10 000 * 50. Элементы этого слоя невидимы, и цель состоит в том, чтобы создавать полосы прокрутки точно так же, как настоящий список.
-
Список видимой области: его можно рассматривать как верхний слой, отображающий обрабатываемые в данный момент данные, высота такая же, как у контейнера визуальной области. Положение видимого списка динамически изменяется, чтобы он всегда отображался в видимой области.
После понимания вышеприведенных концепций давайте посмотрим, что нам нужно делать, когда полоса прокрутки прокручивается:
- в зависимости от расстояния прокрутки и
item
Высота, вычислить высоту списка, который должен отображаться в данный моментstartIndex
- согласно с
startIndex
И высоту видимой области, рассчитать текущий список для отображенияendIndex
- согласно с
startIndex
а такжеendIndex
Перехватите соответствующие данные списка, назначьте их списку видимой области и отобразите на странице. - в зависимости от расстояния прокрутки и
item
Высота, вычислить расстояние смещения списка видимой областиstartOffset
, и занесите его в список
Это принципы, я не знаю, все ли их понимают. Как говорится "Сто раз прочитай книгу, и смысл ее проявится", но я больше верю в "практика приносит истинные знания", тогда давайте реализовывать виртуальный список сами!
Написание краткой версии виртуального списка от руки
src/components/myVirtualScroller.vue
<template>
<!-- 最底层的可视区容器 -->
<div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
<!-- 中间的可滚动区域,z-index=-1,高度和真实列表相同,目的是出现相同的滚动条 -->
<div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }"></div>
<!-- 最上层的可视区列表,数据和偏移距离随着滚动距离的变化而变化 -->
<div class="infinite-list" :style="{ transform: getTransform }">
<div
class="infinite-list-item"
v-for="item in visibleData"
:key="item.id"
:style="{ height: itemSize + 'px' }"
>
{{ item.label }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'MyVirtualList',
props: {
//列表数据
items: {
type: Array,
default: () => []
},
//列表项高度
itemSize: {
type: Number,
default: 50
}
},
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;
z-index: -1;
}
.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>
src/App.vue
<template>
<div class="container">
<my-virtual-scroller :items="list" />
</div>
</template>
<script>
import myVirtualScroller from '@/components/myVirtualScroller'
// 模拟一个长列表
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>
Эффект в основном такой же, как и выше:
Откройте консоль, и вы увидите, что страница всегда отображает только около 6 фрагментов данных:
Выше приведен краткий вариант виртуального списка, можете попробовать сами. Если вы хотите сделать его более совершенным, вам также необходимо учитывать буферную область, элементы списка с высокой степенью адаптивности и т. Д. Заинтересованные студенты могут изучить его самостоятельно.
На сегодня все, я думаю, вы должны знать, как действовать в этой ситуации в следующий раз. Ваша поддержка - моя самая большая мотивация. Если статья была вам полезна, не забудьте поставить бесплатный лайк~😉