数万条数据优化

当一个页面的列表有大量数据如有 100000 条如何优化渲染?

当我们将 10W 条全部在页面加载的时候渲染出来,可以很明显的感受到加载速度缓慢并且可以看到 DOM 树非常非常的长当快速上下滚动时候,偶尔还会卡顿,下面我们用代码实现下这里用 vue 实现

<template>
  <div class="list-view">
    <!-- @scroll="handleScroll($event)" -->
    <div class="list-view-item" :style="{ height: itemHeight + 'px' }" v-for="item in data" :key="item.value">{{ item }}</div>
  </div>
</template>
​
<style>
.list-view {
  height: 600px;
  width: 200px;
  overflow: auto;
  position: relative;
  border: 1px solid #666;
}
​ .list-view-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}
.list-view-content {
  position: absolute;
  height: 600px;
  z-index: 99;
  left: 0;
  right: 0;
  top: 0;
}
​ .list-view-item {
  padding: 5px;
  color: #000;
  height: 30px;
  width: 200px;
  line-height: 30px;
  box-sizing: border-box;
}
</style>
<script>
export default {
  props: {
    itemHeight: {
      type: Number,
      default: 30
    }
  },
  mounted() {},
  created() {
    for (let i = 0; i < 100000; i++) {
      this.data.push('第' + i + '条');
    }
  },
  data() {
    return {
      data: []
    };
  },
  methods: {}
};
</script>

效果图
cc
可以很明显的看到当快速滑动时画面的卡顿

如何优化?

我们知道屏幕都是有一个可视范围的,我们因此就可以当节点在可视范围时就显示出来,当节点不在可视范围时候就移除,因此我们只要渲染可视范围的节点即可
画个图
1552636795(1)

主要分以下几个步骤。 1.计算窗口可视范围,并根据可视范围计算出一屏幕可见的节点。 2.监听滚动计算每次滚动完成后的初始节点和末尾节点 3.根据新的起始节点替换可视区域数据
具体实现参考了一篇网上的博客具体链接会在下面注明:

<template>
  <div class="list-view" @scroll="handleScroll($event)">
    <div class="list-view-phantom" :style="{ height: listheight + 'px' }"></div>
    <div ref="content" class="list-view-content">
      <div class="list-view-item" :style="{ height: itemHeight + 'px' }" v-for="item in visibleData" :key="item.value">{{ item }}</div>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    itemHeight: {
      type: Number,
      default: 30
    }
  },
  mounted() {
    this.visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight); //计算可视区域高度可显示数据条目数
    this.start = 0; //起始数据下标
    this.end = this.start + this.visibleCount; //末尾最后一条
    this.visibleData = this.data.slice(this.start, this.end); // 初始化可视区域数据
  },
  created() {
    for (let i = 0; i < 100000; i++) {
      this.data.push('第' + i + '条'); //初始化10W条数据
    }
    this.listheight = this.data.length * 30; //计算10w条元素的总高度
  },
  data() {
    return {
      data: [],
      start: 0,
      end: null,
      visibleCount: null, //
      visibleData: [],
      scrollTop: 0,
      listheight: ''
    };
  },
  methods: {
    //滚动监听
    handleScroll(event) {
      const scrollTop = this.$el.scrollTop; //监听距离顶部高度
      const fixedScrollTop = scrollTop - (scrollTop % 30); //修复移动位置不精确问题
      this.$refs.content.style.webkitTransform = `translate3d(0, ${fixedScrollTop}px, 0)`; //实时位移
      this.start = Math.floor(scrollTop / 30); //重新计算起始条目下标
      this.end = this.start + this.visibleCount; // 重新计算末尾小标
      this.visibleData = this.data.slice(this.start, this.end); //重新装载可视区域数据
    }
  }
};
</script>
<style>
.list-view {
  height: 400px;
  width: 200px;
  overflow: auto;
  position: relative;
  border: 1px solid #666;
}
​ .list-view-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}
.list-view-content {
  position: absolute;
  height: 400px;
  z-index: 99;
  left: 0;
  right: 0;
  top: 0;
}
​ .list-view-item {
  padding: 5px;
  color: #000;
  height: 30px;
  width: 200px;
  line-height: 30px;
  box-sizing: border-box;
}
</style>

来看看实现后的快速滚动的效果
ccc
aaa

这里主要利用了绝对布局,利用两个绝对布局一个没有任何元素,但高度为所有节点高度的合,利用这个撑开父节点使其滚动条出现,第二个节点高度和可视区域高度一致,当不断滚动的时候利用位移调整位置。
画个图
1552638608(1)

无论如何滑动,可视区域的范围就那么大,因此不会造成页面大量 DOM 节点的存在导致卡顿问题的出现
参考实现https://zhuanlan.zhihu.com/p/26022258