• 重组件的优化和页面渲染十万条数据


    重组件的优化

    以下代码原理是使用requestAnimationFrame(callback) 方法在这里插入图片描述

    vue2写法

    Test01.vue

    <template>
      <div class="container">
        <div v-for="n in 100" :key="n">
          <HeavyCom v-if="defer(n)"></HeavyCom>
        </div>
      </div>
    </template>
    
    <script>
    import HeavyCom from "@/views/HeavyCom";
    
    export default {
      components: { HeavyCom },
      data() {
        return {
          frameCount: 0,
        };
      },
      created() {
        this.updateFrameCount();
      },
      methods: {
        updateFrameCount() {
          const maxCount = 100;
          this.frameCount++;
          if (this.frameCount < maxCount) {
            requestAnimationFrame(this.updateFrameCount);
          }
        },
        defer(n) {
          return this.frameCount >= n;
        },
      },
    };
    </script>
    
    <style>
    .container {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-gap: 1em;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    子组件HeavyCom.vue

    <template>
      <div class="item-container">
        <div class="item" v-for="n in 5000" :key="n"></div>
      </div>
    </template>
    
    <script>
    export default {
      name: "HeavyCom"
    }
    </script>
    
    <style lang="scss">
    .item-container {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      border: 3px solid #f40;
      width: 600px;
      height: 600px;
      box-sizing: border-box;
      .item {
        width: 4px;
        height: 4px;
        background: #ccc;
        margin: 1px;
      }
    }
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    vue3写法

    Test01.vue

    <template>
      <div class="container">
        <div v-for="n in 100" :key="n">
          <HeavyComp v-if="defer(n)"></HeavyComp>
        </div>
      </div>
    </template>
    <script setup>
    import HeavyComp from "@/views/HeavyComp";
    import { useDefer } from './useDefer'
    const defer = useDefer();
    </script>
    
    <style>
    .container {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-gap: 1em;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    子组件HeavyComp.vue

    <template>
      <div class="item-container">
        <div class="item" v-for="n in 5000" :key="n"></div>
      </div>
    </template>
    
    <style>
    .item-container {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      border: 3px solid #f40;
    }
    .item {
      width: 5px;
      height: 3px;
      background: #ccc;
      margin: 0.1em;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    useDefer.js

    import {ref, onUnmounted} from "vue";
    export function useDefer(maxCount = 100) {
        const frameCount = ref(0);
        let rafId;
        function updateFrameCount() {
            rafId = requestAnimationFrame(() => {
                frameCount.value++
                if (frameCount.value >= maxCount) {
                    return;
                }
                updateFrameCount();
            });
        }
        updateFrameCount();
        onUnmounted(() => {
            cancelAnimationFrame(rafId);
        });
        return function defer(n) {
            console.log(frameCount.value, n);
            return frameCount.value >= n;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结果:这里为了录制gif不卡顿,只渲染50个重组件
    优点: 首屏渲染很快,后续的再依次加载
    缺点: 快速滚动时,会出现短暂的白屏
    在这里插入图片描述

    页面渲染十万条数据的优化

    使用虚拟列表的方式

    通过这张图来表示虚拟列表,红框代表你的手机,黑条代表一条条数据
    在这里插入图片描述

    思路:我们只要知道手机屏幕最多能放下几条数据,当下拉滑动时,通过双指针的方式截取相应的数据就可以了。
    🚩 PS:为了防止滑动过快导致的白屏现象,我们可以使用预加载的方式多加载一些数据出来。

    在Test02.vue

    <template>
      <div ref="list" class="v-scroll" @scroll="scrollEvent($event)">
        <div class="infinite-list" :style="{ height: listHeight + 'px' }"></div>
    
        <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>
    </template>
    
    <script>
    export default {
      name: "Test02",
      data() {
        return {
          // 数据
          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 `translate(0,${this.startOffset}px)`;
        },
        //获取真实显示列表数据
        visibleData() {
          return this.listData.slice(this.start, Math.min(this.end, this.listData.length));
        }
      },
      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);
        }
      },
      mounted() {
        for (let i = 1; i <= 100000; i++) {
          this.listData.push({id: i, msg: i})
        }
        this.start = 0;
        this.end = this.start + this.visibleCount;
      }
    }
    </script>
    
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    结果:这里为了录制gif不卡顿,只渲染一万条
    优点:页面上只渲染出可视区域的内容,非常丝滑,无白屏现象
    在这里插入图片描述

  • 相关阅读:
    redis的一些操作
    710. 黑名单中的随机数
    小众调度器
    关于vue中关于eslint报错的问题
    【DS】数据结构八股文英文版(1)
    FTP(数据共享)
    SwiftUI中应用Hero动画(Hero Animation)时一些需要填的坑
    docker知识点扫盲
    好客租房120-在脚手架中使用sass
    【Windows】你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问,这些策略可帮助保护你的电脑
  • 原文地址:https://blog.csdn.net/weixin_53483257/article/details/133982222