vue2实现虚拟列表
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节点数一直没有改变。