虚拟滚动列表


有一个很长的列表需要展示,如果是全部直接全部展示,会因为一次性创建了太多的DOM节点,从而导致卡死。(除非分页)
解决办法是: 采用虚拟滚动。
比如有1K条数据,但是我们可以看到的部分可能只有几十条,所以,那些我们看不到的dom节点,完全没有必要去渲染。所以,虚拟滚动就是只渲染可视部分的dom节点,在滚动的时候,不断地改变可视区域的值即可

以下采用的原理是:

  1. 将wrapper层设置固定高度,并内部可滚动

  2. 内部分三个部分
    上部隐藏区:相当于滚动后隐藏的部分
    实际展示区:实际渲染的内容
    下部隐藏区:未展示的部分

  3. 每次滚动,动态设置,上部隐藏区和下部隐藏区的高度,以及实际展示内容的索引

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
	
	<style type="text/css">
		.wrapper {
		  height: 100%;
		  overflow: auto;
		}
		
		.item {
		  height: 50px;
		  padding-left: 50px;
		}
	</style>
</head>
<body>
	
	<div id="app">
	  <div class="wrapper" ref="wrapper" @scroll="handleScroll" :style="{ height: height + 'px' }">
	    <div ref="w">
	      <div :style="{ height: `${shimTopOffset}px` }"></div>
	      <div
	        class="item"
	        v-for="item in showList"
	        :key="item.index"
	        :style="{
	          height: `${itemHeight}px`,
	          color: item.color,
	        }"
	      >
	        {{ item.index }}
	      </div>
	      <div :style="{ height: `${shimBottomOffset}px` }"></div>
	    </div>
	  </div>
	</div>
	
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
	new Vue({
	  el: '#app',
	  data() {
	    return {
	      height: 500,
	      itemHeight: 50,
	      data: Array.from({ length: 6000 }).map((_, index) => ({
	        index,
	        color: `#${Math.random()
	          .toString(16)
	          .substr(2, 6)}`,
	      })),
	      showList: [],
	      shimTopOffset: 0,
	      shimBottomOffset: 0,
	    };
	  },
	  mounted() {
	    this.update(0);
	    this.$nextTick(() => {
	      this.maxHeight = this.$refs.w.offsetHeight;
	    });
	  },
	  methods: {
	    handleScroll() {
	      const scrollTop = this.$refs.wrapper.scrollTop;
	      if (scrollTop >= 0 && scrollTop + this.height <= this.maxHeight) {
	        window.requestAnimationFrame(() => {
	          this.update(scrollTop);
	        });
	      }
	    },
	    update(scrollTop) {
	      const visibleStart = scrollTop;
	      const visibleEnd = scrollTop + this.height;
	      this.showList = this.getShowList(visibleStart, visibleEnd, this.data);
	    },
	    getShowList(start, end, data) {
	      if (start < end) {
	        const lo = this.getStartIndex(start, this.itemHeight);
	        const hi = this.getEndIndex(end, this.itemHeight);
	        this.shimTopOffset = lo >= 0 ? lo * this.itemHeight : 0;
	        this.shimBottomOffset = hi >= 0 ? (data.length - hi) * this.itemHeight : 0;
	        return data.slice(lo, hi);
	      } else {
	        this.shimTopOffset = 0;
	        this.shimBottomOffset = 0;
	        return [];
	      }
	    },
	    getStartIndex(s, itemHeight) {
	      const startIndex = ~~(s / itemHeight);
	      return startIndex >= 0 ? startIndex : 0;
	    },
	    getEndIndex(e, itemHeight) {
	      const endIndex = Math.ceil(e / itemHeight);
	      return endIndex <= this.data.length ? endIndex : this.data.length;
	    },
	  },
	  watch: {
	    data: {
	      handler(newVal, oldVal) {
	        if (oldVal) {
	          this.$nextTick(() => {
	            this.$refs.wrapper.scrollTop = 0;
	            this.handleScroll();
	          });
	        }
	      },
	      immediate: true,
	    },
	    itemHeight() {
	      this.handleScroll();
	    },
	  },
	})
</script>
</body>
</html>

https://www.leevii.com/2019/11/virtual-scrolling-list.html


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM