*本文参考https://mp.weixin.qq.com/s/3euILxqRsxkOCvK6fBu2xg里面讲述的比较详细

可选方案

1.懒加载(上拉加载)

通过懒加载的方式,在出现长列表的时候,第一次并不完全渲染所有的DOM节点,即可解决一部分场景下的问题。

优点:实现简单

缺点:

1.想要定位到某一个位置的数据会非常困难

1.如果一直加载到底,那么最终还是会出现大量的DOM节点,导致滚动不流畅

2.虚拟渲染

第三方库
vue-virtual-scroller、react-tiny-virtual-list、react-virtualized

2.1 原理

它们的实现原理是利用视差和错觉制作一份出一份“虚拟”列表,一个虚拟列表由三部分组成(拿到列表总高度,通过滚动条滚动的距离来动态计算所展示的列表)

1.视窗口(用户所看到的区域)

2.虚拟数据列表(数据展示)

3.滚动占位区块(底部滚动区)

下面实现一个简单的虚拟渲染
HTML

	<!-- 虚拟滚动 -->
    <div class="virtual-scroll" @scroll.passive="onScroll">
      <div class="over-hide" 
      	:style="{
      		'height': `${data.length * itemHeight}px`
      	}">
        <ul :style="${'margin-top': `${scrollTop}px`}" class="virtual-box">
          <li class="virtual-item"
	         v-for="i in virtualData" 
	         :key="i">
            {{i.item}}
         </li>
        </ul>
      </div>
    </div>

JS

export default {
  data() {
    return {
      scrollTop: 0, // 滚动的距离
      itemHeight: 40, // 每个列表的高度
      startIndex: 0, // 开始截取的下标
      endIndex: 10, // 结束截取的下标
      totalLength: 10000, // 列表总长度
      viewLength: 10 // 当DOM渲染的数量
    }
  },
  computed: {
    data() {
      const list = [...Array(this.totalLength || 0).keys()].map(i => ({
        item: `列表${i}`,
        key: i
      }))
      return list
    },
    virtualData() {
      return this.data.slice(this.startIndex, this.endIndex)
    }
  },
  methods: {
    onScroll(e) {
      let scrollTop = e.target.scrollTop
      this.startIndex = Math.floor(scrollTop / this.itemHeight)
      this.scrollTop = this.startIndex * this.itemHeight
      this.endIndex = this.startIndex + this.viewLength
    }
  }
}

CSS

	.virtual-scroll {
	  width: 300px;
	  height: 200px;
	  overflow-y: auto;
	}
	.over-hide {
	  overflow:hidden;
	}
	.virtual-box {
	  padding: 0;
	  margin: 0;
	  border: 1px solid #e5e5e5;
	  list-style: none;
	}
	.virtual-item {
	  box-sizing: border-box;
	  width: 100%;
	  height: 40px;
	  line-height: 40px;
	  border-bottom: 1px solid #e5e5e5;
	}

扩展另一种写法

HTML

	<!-- 虚拟滚动 -->
    <div class="virtual-scroll" @scroll.passive="onScroll">
      <div class="over-hide" 
      	:style="{
      		'padding': `${scrollTop}px 0 ${data.length * itemHeight - overTop}px`
      	}">
        <ul  class="virtual-box">
          <li class="virtual-item"
	         v-for="i in virtualData" 
	         :key="i">
            {{i.item}}
         </li>
        </ul>
      </div>
    </div>

JS

export default {
  data() {
    return {
      scrollTop: 0, // 滚动的距离
      itemHeight: 40, // 每个列表的高度
      startIndex: 0, // 开始截取的下标
      endIndex: 10, // 结束截取的下标
      totalLength: 10000, // 列表总成度
      viewLength: 10, // 当DOM渲染的数量
      overTop: 0, // 当前滚动的高度+++++++++++++++++++++++
    }
  },
  computed: {
    data() {
      const list = [...Array(this.totalLength || 0).keys()].map(i => ({
        item: `列表${i}`,
        key: i
      }))
      return list
    },
    virtualData() {
      return this.data.slice(this.startIndex, this.endIndex)
    }
  },
  methods: {
    onScroll(e) {
      let scrollTop = e.target.scrollTop
      this.startIndex = Math.floor(scrollTop / this.itemHeight)
      this.endIndex = this.startIndex + this.viewLength
      this.scrollTop = this.startIndex * this.itemHeight
      this.overTop = this.startIndex * this.itemHeight // ++++++++++++++++++++
      if (this.endIndex > this.data.length) {
      	this.overTop = this.data.length * this.itemHeight
      	this.endIndex = this.data.length
	  }
    }
  }
}

频繁触发滚动事件优化


    onScroll(e) {
      let scrollTop = e.target.scrollTop
      let startIndex = Math.floor(scrollTop / this.itemHeight)
      let endIndex = startIndex + this.viewLength
      let currentScrollTop = startIndex * this.itemHeight // 当前滚动高度
      let scroll = this.itemHeight * (this.viewLength - 1) // // 滚动多少距离后 改变位置和列表
      // 如果往下滚了可视区域的一部分,或者往上滚任意距离
      if (currentScrollTop - this.scrollTop >  scroll || currentScrollTop - this.scrollTop < 0) {
        this.scrollTop = currentScrollTop
        this.startIndex = startIndex
        this.endIndex = endIndex
        this.overTop = currentScrollTop
     	if (endIndex > this.data.length) this.overTop = this.data.length * this.itemHeight
      }
    }
    
Logo

前往低代码交流专区

更多推荐