以下是"vue-virtual-scroll-list虚拟组件实现思路详解"的攻略:
什么是vue-virtual-scroll-list
vue-virtual-scroll-list
是一个基于 Vue.js 的虚拟滚动列表组件。 它通过渲染一部分可见的滚动视图,并随着滚动将视图进行重用,从而提高了大型数据列表的性能。
如何使用vue-virtual-scroll-list
vue-virtual-scroll-list
的使用非常简单,只需要通过npm安装组件,然后在Vue组件内引用即可:
npm install vue-virtual-scroll-list --save
<template>
<div>
<virtual-scroll-list :size="50" :remain="10" :bench="20" :tolerance="0" :list="longList">
<template slot-scope="{ item }">
<div>{{ item }}</div>
</template>
</virtual-scroll-list>
</div>
</template>
<script>
import VirtualScrollList from 'vue-virtual-scroll-list'
export default {
components: {
VirtualScrollList
},
data () {
return {
longList: [/* 一大堆数据 */]
}
}
}
</script>
上述代码中,我们先引入一个 VirtualScrollList
组件,然后在模板中使用这个组件,并通过 list
属性将数据传入组件中。在 <template>
中使用了 slot-scope
来对每一项数据 item
进行自定义渲染。
vue-virtual-scroll-list 的实现思路
vue-virtual-scroll-list
的实现非常巧妙,主要是通过一些算法和策略来实现的。下面我们就来详细解析一下其实现思路:
1. 准备工作
在渲染列表前,我们需要先定义一些参数。
scroll
: 此参数是列表容器,通过获取此元素的 scrollTop 和 offsetHeight 属性来监测滚动位置和容器高度。cache
: 一个对象缓存了所有已经渲染的子元素,以及它们的位置和大小信息,后面滚动到哪里,都不需要重新渲染和计算这些子元素的大小和位置。
data() {
return {
scroll: null,
cache: {
start: 0,
end: -1,
size: 0
},
}
},
mounted () {
this.scroll = this.$refs.outer // 获取滚动元素引用
this.scroll.addEventListener('scroll', this.handleScroll) // 监听滚动时间
this.animationFrame = window.requestAnimationFrame(this.handleScroll) // 强制渲染第一屏
this.cache = this.initCache()
},
methods: {
handleScroll() {/*...*/}
initCache() {/*...*/}
}
2. 初次渲染
当组件第一次渲染时,我们不得不渲染出整个列表,并记录下每一个子元素的大小和位置信息。这一步会比较耗性能,但它只需要执行一次,所以耗时可以接受。
initCache() {
const ref = this.$refs.content
const total = this.list.length
let heights = new Array(total)
let positions = new Array(total)
let accHeight = 0
for (let i = 0; i < total; i++) {
const height = ref.children[i].offsetHeight
heights[i] = height
positions[i] = accHeight
accHeight += height
}
return {
start: 0,
end: this.bench, // 预切换点
heights,
positions,
size: accHeight
}
}
在 initCache
函数中,我们通过循环记录每一个子元素的高度和位置信息,然后存储在 cache
变量中。start
和 end
表示当前页面中渲染的子元素范围,heights
和 positions
分别表示每一个子元素的高度和相对于滚动容器顶部的位置。
3. 视图切换
一旦列表开始滚动,就会触发 handleScroll
方法。我们会在这个方法中计算当前需要渲染的子元素范围,并将未滚动到的子元素从页面中移除。
handleScroll() {
const { start, end, cache } = this.cache
const outerHeight = this.scroll.offsetHeight
const scrollTop = this.scroll.scrollTop
const containerTop = this.$refs.wrapper.getBoundingClientRect().top
const threshold = this.tolerance // 阀值
let newStart = start
let newEnd = end
if (scrollTop < cache.positions[start] - containerTop) { // 向上拖拽
newStart = this.findStartIndex(scrollTop - containerTop)
if (newStart < start) { // 省去无效渲染
newEnd = Math.min(
Math.max(newStart + this.remain, end),
cache.positions.length - 1
)
}
} else if (scrollTop + outerHeight > cache.positions[end] - containerTop + threshold) { // 向下拖拽
newEnd = this.findEndIndex(scrollTop + outerHeight - containerTop + threshold)
if (newEnd > end) { // 省去无效渲染
newStart = Math.max(
Math.min(newEnd - this.remain, start),
0
)
}
}
// 将未显示的元素移除
for (let i = start; i < newStart; i++) {
this.removeItem(i)
}
for (let i = end; i > newEnd; i--) {
this.removeItem(i)
}
// 将可见元素添加到页面中
for (let i = newStart; i < start; i++) {
this.createItem(i)
}
for (let i = end + 1; i <= newEnd; i++) {
this.createItem(i)
}
// 更新 cache
if (newStart !== start || newEnd !== end) {
this.cache = {
start: newStart,
end: newEnd,
size: cache.size,
positions: cache.positions,
heights: cache.heights
}
}
},
在 handleScroll
方法中,首先会计算处于可视区域内的子元素范围(newStart
和 newEnd
)。如果可见区域发生了变化,我们需要将未展示的子元素从页面中移除,然后将可见的子元素添加至页面中。
4. 子元素重用
现在,我们已经解决了需要渲染哪些元素的问题。但是,每次滚动到新的区域时,需要重新渲染子元素是非常低效的。为了优化这一点,我们需要通过重用先前已经渲染的子元素来实现。
removeItem(index) {
const node = this.$refs.content.children[index]
this.placeholder.appendChild(node)
this.cache.size -= this.cache.heights[index]
},
createItem(index) {
const node =
this.$refs[this.slotName] &&
this.$refs[this.slotName][index] &&
this.$refs[this.slotName][index][0] // 找到对应列表项的索引,通过v-show控制是否显示
if (!node) return
this.placeholder.appendChild(node.cloneNode(true))
this.cache.size += this.cache.heights[index]
},
在 removeItem
和 createItem
方法中,我们分别将子元素从页面中移除和添加回页面。需要注意的是,每一个移除的子元素,都要从 cache.size
中减去其自身的高度;每一个添加的子元素,都要从 cache.size
中加上其自身的高度。
示例说明
- 示例1:假设需要渲染 10000 条数据,采用传统的渲染方式,肯定会卡顿。而使用
vue-virtual-scroll-list
虚拟列表组件,只会渲染出当前可视区域的元素,因此性能会大大提升。 - 示例2:为了方便入门学习,我们可以参考
vue-virtual-scroll-list
源码,自己编写一个类似的虚拟列表组件。不仅能更深入理解其实现原理,还能够定制化更各式各样的外观和功能。
总结
以上就是 vue-virtual-scroll-list
虚拟列表组件的实现思路和代码示例。虽然组件实现思路有些复杂,但是通过实现,可以高效地渲染大量数据。在后续的开发过程中,可以参考和借鉴这样的思路,来处理大量数据的显示问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue-virtual-scroll-list虚拟组件实现思路详解 - Python技术站