1.vue-virtual-scroll-list插件
- 这个插件就是vue中的一个长列表的插件,官网地址:https://tangbc.github.io/vue-virtual-scroll-list/#/
- 来看一下该组件的渲染情况:
- 可以明显看出,其渲染的时候,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组件上面有几个参数,分别介绍一下这几个参数的意义:
- items:要进行渲染的列表数据;
- size:每一条数据的高度;
- 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>
注意几点:
- 接收父组件传递过来的数据,然后我声明了两个变量,start、end,这两个就是为了每次进行渲染要显示的数据,在items数组中的起始结束下标位置;这两的长度固定在shownumber个单位以内;
- 可以发现我对container容器设置了一个高度,因为在真实的开发中,一般就是在一块区域中进行展示列表数据,所以把这个模拟成一个页面的小框框区域,我设置的其高度就是shownumber个列表项的高度,刚好让shownumber个数据完全展示出来;
- 我在页面中还创建了一个类名为bar的div节点,这个是为了撑开整个容器的高度,让其有一个滚动的区域,高度就是整个items数据的长度×每个列表项的高度size
- 先来看页面的效果:
Ⅱ. 虚拟列表制作
- 给容器绑定一个滚动事件,当容器发生滚动的时候,就让其动态的去渲染后续的数据
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节点数一直没有改变。