数据量特别大,自己手写虚拟dom节点展示数据。

当数据量特别大时,一次性全部展示出所有数据,会造成页面渲染慢,白屏,卡顿的现象。严重影响体验。为解决这样的问题,可以尝试使用虚拟加载dom节点的方式。

原理:根据屏幕高度和一条数据展示所需要的dom节点高度,求出屏幕能展示的数据条数。在渲染数据的div的同级加一个div(高度为所有数据展示完的高度),目的是显示滚动条。监听滚动,根据滚动条位置,计算截取所有数据中能在屏幕中展示的数据段,进行渲染。

代码demo:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/vue/dist/vue.global.js"></script>
  <title>虚拟列表</title>
  <style>
    .v-scroll {
      height: 600px;
      width: 400px;
      border: 3px solid #000;
      overflow: auto;
      position: relative;
      -webkit-overflow-scrolling: touch;
    }

    .infinite-list {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      z-index: -1;
    }

    .scroll-list {
      left: 0;
      right: 0;
      top: 0;
      position: absolute;
      text-align: center;
    }

    .scroll-item {
      padding: 10px;
      color: #555;
      box-sizing: border-box;
      border-bottom: 1px solid #999;
    }
  </style>
</head>

<body>
  <div id="app">
    <!--.v-scroll盒子高度固定,目的:出现滚动条-->
    <div ref="list" class="v-scroll" @scroll="scrollEvent($event)">

      <!--.infinite-list绝对定位高度为所有数据渲染需要的高度,目的:出现滚动条-->
      <div class="infinite-list" :style="{ height: listHeight + 'px' }"></div>
      
      <!--要渲染的真实数据的dom-->
      <div class="scroll-list" :style="{ transform: getTransform }">
        <div ref="items" class="scroll-item" v-for="item in visibleData" :key="item.id"
          :style="{ height: itemHeight + 'px',lineHeight: itemHeight + 'px' }">{{ item.msg }}</div>
      </div>
    </div>
  </div>

  <script>
    let listData = []
    for (let i = 1; i <= 10000000; i++) {
      listData.push({
        id: i,
        msg: i + ':真实渲染节点'+i
      })
    }

    const { createApp } = Vue
    createApp({
      data() {
        return {
          listData: listData,
          itemHeight: 60,
          //可视区域高度
          screenHeight: 600,
          //偏移量
          startOffset: 0,
          //起始索引
          start: 0,
          //结束索引
          end: null,
        };
      },
      computed: {
        //列表总高度
        listHeight() {
          return this.listData.length * this.itemHeight;
        },
        //可显示的列表项数
        visibleCount() {
          return Math.ceil(this.screenHeight / this.itemHeight)
        },
        //偏移量对应的style
        getTransform() {
          return `translate3d(0,${this.startOffset}px,0)`;
        },
        //获取真实显示列表数据(从全部数据中截取)
        visibleData() {
          return this.listData.slice(this.start, Math.min(this.end, this.listData.length));
        }
      },
      mounted() {
        this.start = 0;
        this.end = this.start + this.visibleCount;
      },
      methods: {
        scrollEvent() {
          //当前滚动位置
          let scrollTop = this.$refs.list.scrollTop;
          //此时的开始索引
          this.start = Math.floor(scrollTop / this.itemHeight);
          //此时的结束索引
          this.end = this.start + this.visibleCount;
          //此时的偏移量
          this.startOffset = scrollTop - (scrollTop % this.itemHeight);
        }
      }
    }).mount('#app')
  </script>
</body>

</html>

优化:可以配合节流函数进行优化,防止暴力滚动。

    var throttle = (func, delay) => {  //节流
      var prev = Date.now();
      return function () {
        var context = this;
        var args = arguments;
        var now = Date.now();
        if (now - prev >= delay) {
          func.apply(context, args);
          prev = Date.now();
        }
      }
    }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值