龙空技术网

听说你还不会虚拟列表?原谅我来晚了

前端阿飞V 96

前言:

今天我们对“div 最上层”大约比较重视,看官们都需要知道一些“div 最上层”的相关文章。那么小编在网摘上汇集了一些对于“div 最上层””的相关资讯,希望看官们能喜欢,咱们快快来学习一下吧!

什么是虚拟列表

虚拟列表是指对列表的 可视区域 进行渲染,对 非可见区域 不渲染或部分渲染,从而极大提高渲染性能的一种技术。

为什么要用虚拟列表

有时我们会遇到一些业务场景,要展示的列表很长,且不能使用分页的方式,如果一次性把数据全部渲染到页面,浏览器将变得非常卡顿,因为渲染 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>

效果如下:

一万条数据的列表瞬间就渲染出来了,滚动也丝滑无比,是不是很nice!

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>

效果如下:

可以看到列表项的高度是随内容的变化而变化,依旧是丝滑无比!

虚拟列表原理

初次听到 “虚拟列表” 这个名词感觉非常的高大上,其实弄清楚它的原理之后,你会发现它非常的简单。话不多说,先上图:

可视区容器:可以看作是在最底层,容纳所有元素的一个盒子。可滚动区域:可以看作是中间层,假设有 10000 条数据,每个列表项的高度是 50,那么可滚动的区域的高度就是 10000 * 50。这一层的元素是不可见的,目的是产生和真实列表一模一样的滚动条。可视区列表:可以看作是在最上层,展示当前处理后的数据,高度和可视区容器相同。可视区列表的位置是动态变化的,为了使其始终出现在可视区域。

理解以上概念之后,我们再看看当滚动条滚动时,我们需要做什么:

根据滚动距离和 item 高度,计算出当前需要展示的列表的 startIndex根据 startIndex 和 可视区高度,计算出当前需要展示的列表的 endIndex根据 startIndexendIndex 截取相应的列表数据,赋值给可视区列表,并渲染在页面上根据滚动距离和 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>

效果和上面基本是一样:

以上就是简版的虚拟列表,大家可以自己动手试一试。如果要做的更加完善,还需考虑缓冲区域、列表项高度自适应等,有兴趣的同学可以自己研究一哈。

今天的分享就到这里,相信下次遇到这种场景你应该知道怎么处理了。你的支持就是我最大的动力,如果文章对你有所帮助,不要忘了点个免费的赞呦~

Demo地址:

标签: #div 最上层