vue2实现虚拟列表

1.vue-virtual-scroll-list插件

  1. 这个插件就是vue中的一个长列表的插件,官网地址:https://tangbc.github.io/vue-virtual-scroll-list/#/

  2. 来看一下该组件的渲染情况:

    在这里插入图片描述

  3. 可以明显看出,其渲染的时候,DOM节点数量都是固定的,并不会将所有的内容全部加载出来

 

2.vue2实现虚拟列表

Ⅰ. 项目搭建

  • 建一个新的文件夹,在这个文件夹中创建一个vue2的项目:vue create vue2-virtual-scroll,模板选择默认的vue2模板即可;
  • 在components目录下创建一个List.vue组件,用来进行虚拟列表的展示;
  • 在App.vue主入口页面中去引入该组件:
 1 <template>
 2   <div id="app">
 3     <List
 4       :items="items"
 5       :size="60"
 6       :shownumber="10"
 7     />
 8   </div>
 9 </template>
10 
11 <script>
12 import List from './components/List.vue'
13 
14 export default {
15   name: 'App',
16   components: {
17     List
18   },
19   computed: {
20     // 要进行渲染的数据列表
21     items () {
22       // 自己模拟一万条数据,将其内容进行填充
23       return Array(10000).fill('').map((item, index) => ({
24         id: index,
25         content: '列表项内容' + index
26       }))
27     }
28   }
29 }
30 </script>
31 
32 <style>
33 #app {
34   font-family: Avenir, Helvetica, Arial, sans-serif;
35   -webkit-font-smoothing: antialiased;
36   -moz-osx-font-smoothing: grayscale;
37   text-align: center;
38   color: #2c3e50;
39   margin-top: 60px;
40 }
41 </style>

注意:

可以发现我的List组件上面有几个参数,分别介绍一下这几个参数的意义:

  1. items:要进行渲染的列表数据;
  2. size:每一条数据的高度;
  3. showNumber:每次渲染的数据条数(DOM个数);

后续还可以继续给这个组件添加属性,用来决定一些数据的性质等
因为没有真实的数据,我在computed计算属性中,通过数组遍历的方式创建了一万条假数据,并且都填充上了值,让这数组中的值充当数据;

  • 先把List.vue虚拟列表页面组件搭建起来:
 1 <template>
 2   <div
 3     class="container"
 4     :style="{ height: containerHeight }"
 5   >
 6     <!-- 数据列表 -->
 7     <div class="list">
 8       <!-- 列表项 -->
 9       <div
10         v-for="item in showData"
11         :key="item.id"
12         :style="{ height: size + 'px' }"
13       >
14         {{ item.content }}
15       </div>
16 
17       <!-- 用于撑开高度的元素 -->
18       <div
19         class="bar"
20         :style="{ height: barHeight }"
21       />
22     </div>
23   </div>
24 </template>
25 
26 <script>
27 export default {
28   name: 'VircualList',
29   props: {
30     // 要渲染的数据
31     items: {
32       type: Array,
33       required: true
34     },
35     // 每条数据渲染的节点的高度
36     size: {
37       type: Number,
38       required: true
39     },
40     // 每次渲染的 DOM 节点个数
41     shownumber: {
42       type: Number,
43       required: true
44     }
45   },
46   data () {
47     return {
48       start: 0, // 要展示的数据的起始下标
49       end: this.shownumber // 要展示的数据的结束下标
50     }
51   },
52   computed: {
53     // 最终筛选出的要展示的数据
54     showData () {
55       return this.items.slice(this.start, this.end)
56     },
57     // 容器的高度
58     containerHeight () {
59       return this.size * this.shownumber + 'px'
60     },
61     // 撑开容器内容高度的元素的高度
62     barHeight () {
63       return this.size * this.items.length + 'px'
64     }
65   }
66 }
67 </script>
68 
69 <style scoped>
70 .container {
71   overflow-y: scroll;
72   background-color: rgb(150, 195, 238);
73   font-size: 20px;
74   font-weight: bold;
75   line-height: 60px;
76 }
77 </style>

注意几点:

  1. 接收父组件传递过来的数据,然后我声明了两个变量,start、end,这两个就是为了每次进行渲染要显示的数据,在items数组中的起始结束下标位置;这两的长度固定在shownumber个单位以内;
  2. 可以发现我对container容器设置了一个高度,因为在真实的开发中,一般就是在一块区域中进行展示列表数据,所以把这个模拟成一个页面的小框框区域,我设置的其高度就是shownumber个列表项的高度,刚好让shownumber个数据完全展示出来;
  3. 我在页面中还创建了一个类名为bar的div节点,这个是为了撑开整个容器的高度,让其有一个滚动的区域,高度就是整个items数据的长度×每个列表项的高度size
  • 先来看页面的效果:

在这里插入图片描述

Ⅱ. 虚拟列表制作

  1. 给容器绑定一个滚动事件,当容器发生滚动的时候,就让其动态的去渲染后续的数据

 1 <template>
 2   <div
 3     class="container"
 4     :style="{ height: containerHeight }"
 5     @scroll="handleScroll"
 6     ref="container"
 7   >
 8     <!-- 数据列表 -->
 9     <div class="list">
10       <!-- 列表项 -->
11       <div
12         v-for="item in showData"
13         :key="item.id"
14         :style="{ height: size + 'px' }"
15       >
16         {{ item.content }}
17       </div>
18 
19       <!-- 用于撑开高度的元素 -->
20       <div
21         class="bar"
22         :style="{ height: barHeight }"
23       />
24     </div>
25   </div>
26 </template>
27 
28 <script>
29 export default {
30   name: 'VircualList',
31   props: {
32     // 要渲染的数据
33     items: {
34       type: Array,
35       required: true
36     },
37     // 每条数据渲染的节点的高度
38     size: {
39       type: Number,
40       required: true
41     },
42     // 每次渲染的 DOM 节点个数
43     shownumber: {
44       type: Number,
45       required: true
46     }
47   },
48   data () {
49     return {
50       start: 0, // 要展示的数据的起始下标
51       end: this.shownumber // 要展示的数据的结束下标
52     }
53   },
54   computed: {
55     // 最终筛选出的要展示的数据
56     showData () {
57       return this.items.slice(this.start, this.end)
58     },
59     // 容器的高度
60     containerHeight () {
61       return this.size * this.shownumber + 'px'
62     },
63     // 撑开容器内容高度的元素的高度
64     barHeight () {
65       return this.size * this.items.length + 'px'
66     }
67   },
68   methods: {
69     // 容器的滚动事件
70     handleScroll () {
71       // 获取容器顶部滚动的尺寸
72       const scrollTop = this.$refs.container.scrollTop
73 
74       // 计算卷去的数据条数,用计算的结果作为获取数据的起始和结束下标
75       // 起始的下标就是卷去的数据条数,向下取整
76       this.start = Math.floor(scrollTop / this.size)
77       // 结束的下标就是起始的下标加上要展示的数据条数
78       this.end = this.start + this.shownumber
79     }
80   }
81 }
82 </script>
83 
84 <style scoped>
85 .container {
86   overflow-y: scroll;
87   background-color: rgb(150, 195, 238);
88   font-size: 20px;
89   font-weight: bold;
90   line-height: 60px;
91 }
92 </style>

在这里插入图片描述


注意:

  • 在每次滚动的时候,就需要去修改要重新渲染的数据的起始和结束下标:
  • 起始下标的计算 = 区域向上卷去的高度 scrollTop ÷每个数据的高度 size ,然后向下取整
  • 结束下标的计算 = 起始的下标 + 页面展示的数据的条数 shownumber

  2.可以发现上图中,数据发生了变化,但是列表还是依旧向上滚动,接下来需要给列表做定位的处理,只需要每次滚动的时候,让列表跟着向下滚动即可

  1 <template>
  2   <div
  3     class="container"
  4     :style="{ height: containerHeight }"
  5     @scroll="handleScroll"
  6     ref="container"
  7   >
  8     <!-- 数据列表 -->
  9     <div
 10       class="list"
 11       :style="{ top: listTop }"
 12     >
 13       <!-- 列表项 -->
 14       <div
 15         v-for="item in showData"
 16         :key="item.id"
 17         :style="{ height: size + 'px' }"
 18       >
 19         {{ item.content }}
 20       </div>
 21 
 22       <!-- 用于撑开高度的元素 -->
 23       <div
 24         class="bar"
 25         :style="{ height: barHeight }"
 26       />
 27     </div>
 28   </div>
 29 </template>
 30 
 31 <script>
 32 export default {
 33   name: 'VircualList',
 34   props: {
 35     // 要渲染的数据
 36     items: {
 37       type: Array,
 38       required: true
 39     },
 40     // 每条数据渲染的节点的高度
 41     size: {
 42       type: Number,
 43       required: true
 44     },
 45     // 每次渲染的 DOM 节点个数
 46     shownumber: {
 47       type: Number,
 48       required: true
 49     }
 50   },
 51   data () {
 52     return {
 53       start: 0, // 要展示的数据的起始下标
 54       end: this.shownumber // 要展示的数据的结束下标
 55     }
 56   },
 57   computed: {
 58     // 最终筛选出的要展示的数据
 59     showData () {
 60       return this.items.slice(this.start, this.end)
 61     },
 62     // 容器的高度
 63     containerHeight () {
 64       return this.size * this.shownumber + 'px'
 65     },
 66     // 撑开容器内容高度的元素的高度
 67     barHeight () {
 68       return this.size * this.items.length + 'px'
 69     },
 70     // 列表向上滚动时要动态改变 top 值
 71     listTop () {
 72       return this.start * this.size + 'px'
 73     }
 74   },
 75   methods: {
 76     // 容器的滚动事件
 77     handleScroll () {
 78       // 获取容器顶部滚动的尺寸
 79       const scrollTop = this.$refs.container.scrollTop
 80 
 81       // 计算卷去的数据条数,用计算的结果作为获取数据的起始和结束下标
 82       // 起始的下标就是卷去的数据条数,向下取整
 83       this.start = Math.floor(scrollTop / this.size)
 84       // 结束的下标就是起始的下标加上要展示的数据条数
 85       this.end = this.start + this.shownumber
 86     }
 87   }
 88 }
 89 </script>
 90 
 91 <style scoped>
 92 .container {
 93   position: relative;
 94   overflow-y: scroll;
 95   background-color: rgb(150, 195, 238);
 96   font-size: 20px;
 97   font-weight: bold;
 98   line-height: 60px;
 99   text-align: center;
100 }
101 
102 .list {
103   position: absolute;
104   top: 0;
105   width: 100%;
106 }
107 </style>

 

注意:列表动态的高度top是当前页面渲染的数据的起始下标 × 每个数据的高度, 即卷上去的列表高度

 

 

  3.来看现在的页面效果:

在这里插入图片描述

 

 

  4.上面就可以很清楚的看出列表项似乎是一直在向下滚动的,但是页面的DOM节点数一直没有改变。

posted @ 2023-02-01 23:52  强者之途  阅读(2821)  评论(1编辑  收藏  举报
/* 看板娘 */