前端开发中的长列表优化

众所周知,浏览器在渲染dom节点的时候,节点越复杂数量越多那么所消耗的时间就会越长。当业务需求为一次性要渲染大量的数据时,数据量过大且一次性全部渲染出来的时候会使浏览器卡顿或者卡死。一般情况下,列表形式的数据展示形式是最常见的,大部分情况下,常见的方式时普通分页加载和无限滚动加载,普通分页加载在这里就不说了大部分后台管理都采用这个方式,无限滚动则是根据鼠标滚动触底时加载下一页数据,实现的思路大致是监听父元素的 scroll 事件(一般是 window),通过父元素的 scrollTop 判断是否到了页面是否到了页面底部,如果到了页面底部,就加载更多的数据。这样做优点就是用户向上翻数据的时候不用点上一页按钮。

非一次性完整渲染长列表

  1. 一是无限滚动加载,就是懒加载,就是在滚动到页面底部的时候,再去加载剩余的数据,说白了也是对数据进行分页,一种常见的首屏优化方案
  2. 另一种是按需加载,假设总数据为1000条,可视区域高度为500px,每个列表item高度为50px,那么在可视区域内只需要渲染10个就可以了,而不是1000条,根据用户滚动在对可视区域的节点位置和下标进行计算,在总数据中进行截取替换,每次只是渲染可视区域的数据节点,懒加载则是一直追加

在饿了么前端发表的文章中看到这么一段话,

你可能会发现无限滚动在移动端很常见,但是可见区域渲染并不常见,这个主要是因为 iOS 上 UIWebView 的 onscroll
事件并不能实时触发。笔者曾尝试过使用 iScroll
来实现类似可视区域渲染,虽然初次渲染慢的问题可以解决,但是会出现滚动时体验不佳的问题(会有白屏时间)。

这个我也在移动端小程序上试过,渲染可视区域的固定数量节点,但是用户快速滑动或者一些其他操作的时候就会出现问题 体验感确实不好

下面给出一个简单demo,关于按需加载,实现方式有很多,也有很多博客技术贴,我这种也参考了其他人的,如果还有其他更好的方案,请告知

下面是ietm为固定高度得demo代码 大部分地方加了备注,都能看懂,写的比较草率,后期会整理成插件

<template>
  <Card :bordered="false" class="content">
    <div class="wraper" ref="wraper" @scroll="handleScroll">
      <div class="scroll-bar" ref="scrollBar" :style="{height:allHeight + 'px'}"></div>
      <ul class="list" ref="list"  :style="{ transform: getTransform }">
        <li class="item" v-for="item in visibaleData" :key="item">{{item}}</li>
      </ul>
    </div>
  </Card>
</template>

<script>
export default {
  name: "baseTable",
  data() {
    return {
      wraperHeight: 600, //可视区域高度
      itemHeight: 60, //每个列得高度
      visibaleCount: 0, //可视区域得渲染得个数
      allHeight: 0, //总数据得高度
      visibaleData: [], //可视区域渲染得数据集合
      list: [], //总数据
      startIndex: 0, //可视区域得第一个元素
      endIndex: 0, //可视区域得最后一个元素
      bufferSize: 6, // 一个缓冲值,用来增加一定的缓存区域,
      getTransform:0,
      scroll:0,
    };
  },
  mounted() {
    // 构造个大数据量得数据集合
    for (let i = 0; i < 1000; i++) {
      this.list.push("列表" + i);
    }
    // 计算显示区域可以渲染得数量
    this.visibaleCount = Math.floor(this.wraperHeight / this.itemHeight) + this.bufferSize
    // 可视区域得最后一个元素
      this.endIndex = this.startIndex + this.visibaleCount
    // 计算全部数据得高度
    this.allHeight = this.list.length * this.itemHeight
    this.updateVisibleData()
  },
  methods: {
    //  此处用计算属性也可以
    updateVisibleData(){
      //可视区域渲染得数据集合
      this.visibaleData = this.list.slice(this.startIndex,this.endIndex)
    },
    // 滚动事件
    handleScroll(e){
      let scroll = this.$refs.wraper.scrollTop
      this.startIndex = Math.floor(scroll / this.itemHeight)
      // 可视区域得最后一个元素
      this.endIndex = this.startIndex + this.visibaleCount
	// 设置top和平移都可以,貌似使用transform性能要好些
      // this.$refs.list.style.top =this.startIndex * this.itemHeight + 'px'
      // 让滚动距离为itemHeight整数倍,
      this.getTransform = `translate3d(0,${scroll - scroll % this.itemHeight}px,0)`
       this.updateVisibleData()
    }
  },
};
</script>
<style lang="less" scoped>
.wraper {
  width: 600px;
  height: 600px;
  background: cadetblue;
  position: relative;
  overflow-y: scroll;
  .list {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    .item {
      height: 60px;
      width: 100%;
      border: 1px solid #000;
      box-sizing: border-box;
    }
  }
}
</style>

你可能感兴趣的:(vue,日常积累)