vue实现虚拟滚动渲染成千上万条数据
永远不会太晚 人气:0html渲染成千上万的dom会导致浏览器卡顿乃至卡死,虚拟滚动就是解决方案之一
页面只需要渲染可视区域的dom,当用户进行滚动时根据滚动高度进行可视区域dom的更新,从而达到无感滚动效果
思路:
- 根据数据条数计算相应高度,使用dom占位,从而生成相应高度滚动列表,用户可以自由滚动
- 监听滚动列表的scroll事件
- 根据scrollTop进行slice数组,取出相应下标数据
- 根据scrollTop值控制虚拟列表偏移,使其始终在用户可视区域内
- 上下需要预留一些dom,避免用户滚动过快出现白屏
- 使用fixed,absolute,transform避免页面回流
代码量其实还是很少的,重点是理解思路
<template> <div class="device"> <div class="vir-scroll"> <div class="scroll-Y" @scroll="scroll"> <div class="parentDom"> <!-- 占位,根据数据条数生成滚动列表 --> <div :style="{ height: screenHeight + 'px' }"></div> <!-- 虚拟滚动列表 --> <div class="positionRelative" :style="{ transform: getTransform }"> <div class="scroll-item" v-for="item in visibleData" :key="item.index"> <div class="scroll-info">helloworld</div> </div> </div> </div> </div> </div> </div> </template> <script> export default { data() { return { /** 数据列表 */ dataList: [], /** 单行高度 */ itemHeight: 143, /** 偏移高度 */ startOffset: 0, /** 起始显示数据 */ start: 0, /** 结束显示数据 */ end: 10, } }, computed: { /** 根据每条数据的高度获取总列表高度 */ screenHeight() { return this.dataList.length * this.itemHeight }, /** 前面预留 */ prevCount() { return 8 }, /** 后面预留 */ nextCount() { return 25 }, /** 每次截取虚拟列表的位置 */ getTransform() { return `translate(0,${this.startOffset}px)` }, /** 虚拟数据 */ visibleData() { return this.dataList.slice(this.start, Math.min(this.end, this.dataList.length)) }, }, methods: { /** 列表滚动,暂时不节流,因为滚动快触发次数就少,容易导致没有及时更新数组导致白屏 */ scroll(e) { this.scrollThrottle(e.target.scrollTop) }, /** 滚动函数 */ scrollThrottle(scrollTop) { const topCount = Math.floor(scrollTop / this.itemHeight) - this.prevCount // 此时的开始索引 this.start = topCount >= 0 ? topCount : 0 // 此时的结束索引 this.end = this.start + this.nextCount // 此时的偏移量 this.startOffset = this.start * this.itemHeight }, }, } </script> <style scoped lang="scss"> .device { .vir-scroll { // 脱离文档流避免回流 position: fixed; left: 0; width: 100%; height: calc(100% - 196px); padding: 0 16px; z-index: 10; .scroll-Y { width: 100%; height: 100%; overflow-y: auto; .parentDom { position: relative; .positionRelative { width: 100%; position: absolute; left: 0; top: 0; border-radius: 5px; padding-bottom: 12.5px; .scroll-item { height: 143px; background-color: #ffffff; font-size: 12px; padding: 10px 15px 0px 15px; } } } } } } </style>
加载全部内容