js优化中的虚拟列表

文章类型:Javascript

发布者:admin

发布时间:2023-05-19

在操作大量数据时,一次性渲染到页面会导致卡顿,不流畅,需要进行优化

一:定义

1:虚拟化渲染和虚拟列表是在处理大量数据时常用的性能优化技术。虚拟化渲染是一种通用的概念,用于指代只渲染可见区域内容的技术,而虚拟列表是一种具体的实现方式,用于处理长列表的优化

二:流程

  1. 确定可视区域:首先确定可视区域的大小,通常是通过计算容器元素的高度和宽度来确定。
  2. 数据分块:将要展示的数据分成多个块,每个块包含一定数量的数据项。块的大小可以根据需求和性能进行调整。
  3. 动态渲染数据:根据可视区域的大小和滚动位置,计算出当前可见的数据块范围。只渲染当前可见的数据块,并且在滚动时动态加载和卸载数据块。
  4. 节点复用:对非可见区域的数据块进行复用。当一个数据块离开可视区域时,将其从DOM中移除并放入一个数据块池中。当新的数据块需要加载到可视区域时,首先从数据块池中查找是否有可复用的数据块,如果有,则更新其内容为新数据块的内容并移动到正确的位置

三:代码

1:第一步:确定视口宽高,也就是确定用户可视区域以及显示多少个,每个行高

2:第二步:计算出滚动条内容高度,通过总长度*每个行高

3:第三步:进行计算属性截取,固定显示设置的个数,冻结对象有利于避免绑定依赖关系

4:第四步:进行绑定滚动事件,计算滚动的位置/每个行高得出滚动的个数,调整起始位置和结束位置

5:第五步:进行滚动位置的平移拉回,往上卷了多少距离,数据变化后像下拉回多少距离

//v3版本
<script  setup lang="ts">
//虚拟列表
import {ref, reactive, onMounted,computed} from 'vue'
//定义大数据源 并且进行对象的冻结,避免vue依赖监听
const list=reactive(Object.freeze(new Array(200000).fill(null).map((item,i)=>({n:i+1}))))
//定义获取操作dom的ref
const  viewport=ref<any>(null)
const  scrollBar=ref<any>(null)
const  showbox=ref<any>(null)
//定义显示的个数以及每个的高度
const viewCount=ref(20)
const  rowHeight=ref(30)
//定义起始位置和结束位置
const  start=ref(0)
const end=ref(20)
onMounted(()=>{
  //计算出总高度和当前显示区域视口高度
  viewport.value.style.height=viewCount.value*rowHeight.value+'px'
  scrollBar.value.style.height=list.length*rowHeight.value+'px'

})
//滚动事件 进行更换数据值,起始位置
const  scrollShow=()=>{
  let scrollTop=viewport.value.scrollTop
  console.log(scrollTop)
  start.value=Math.round(scrollTop/rowHeight.value)
  end.value=start.value+viewCount.value
  //向上卷多少个,往下拉多少距离
  showbox.value.style.transform=`translateY(${scrollTop}px)`
}
//计算要显示出的个数
const showList=computed(()=>(list.slice(start.value,end.value)))

</script>
<template>
  <div>


<div class="box" ref="viewport" :style="{'--rowHeight':rowHeight+'px'}" @scroll="scrollShow">
  <div class="scrollbox" ref="scrollBar"></div>
  <div class="rowbox" ref="showbox">
    <p class="row"  v-for="(item,index) in showList" :key="item.n">{{item.n}}</p>
  </div>
</div>
  </div>
</template>



<style lang="less" scoped>
.box{
  width: 400px;
  height: 500px;
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  overflow-y: scroll;
  background: gainsboro;
}
p{
  margin: 0;
}
.rowbox{
  position: absolute;
  left: 0;
  top: 0;

}
.row{
  height: var(--rowHeight);
}
</style>
//v2版本
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			.box{
				width: 300px;
				height: 400px;
				position: fixed;
				margin: auto;
				overflow-y: scroll;
				left: 0;
				right: 0;
				bottom: 0;
				top: 0;
				background: gainsboro;
			}
			.scroller{
				/* height: 3000px; */
			}
			.list{
				position: absolute;
				left: 0;
				top: 0;
			}
			.row{
				margin: 0 ;
				height: var(--rowheight);
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div class="box" ref="viewport" :style="{'--rowheight':rowHeight+'px'}" @scroll="onScroll">
				<div class="scroller" ref="scrollbar"></div>
				<div class="list" ref="list">
						<p class="row" v-for="(item,index) in showList" >{{item.n}}</p>
				</div>
			
			</div>
			

			
		</div>
	</body>
</html>
<script src="js/vue.js"></script>
<script>
	const bigList=new Array(10000).fill(null).map((item,i)=>({n:i+1}))

	new Vue({
		el:'#app',
		computed:{
			showList(){
				return this.list.slice(this.start,this.end)
			}	
		},
		data(){
			return{
				
				list:Object.freeze(bigList),
				 viewCount:20,
				 rowHeight:30,
				 start:0,
				 end:20
			}
		},
		methods:{
			onScroll(){
				let offsettop=this.$refs.viewport.scrollTop
				console.log(offsettop)
				this.start=Math.round(offsettop/this.rowHeight)
				this.end=this.start+this.viewCount
				this.$refs.list.style.transform=`translateY(${offsettop}px)`
				
			}	
		},
		mounted(){
			this.$refs.viewport.style.height=this.rowHeight*this.viewCount+'px'
			this.$refs.scrollbar.style.height=this.rowHeight*this.list.length+'px'
		}
	})
</script>


四:总结

  1. 减少了实际渲染的节点数量,提高了页面的性能和加载速度。
  2. 对于大型列表,只有当前可见的数据项被渲染到页面中,节省了内存和资源消耗。
  3. 通过动态加载和卸载节点,减少了渲染和更新的时间开销。
  4. 可以结合缓冲区、节点复用和懒加载等技术来进一步优化性能。
评论
0条评论遵守法律,文明用语,共同建设文明评论区

暂无评论,快来发表第一条评论吧~