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

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

重组件的优化

以下代码原理是使用requestAnimationFrame(callback) 方法

vue2写法

Test01.vue

javascript 复制代码
<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>

子组件HeavyCom.vue

javascript 复制代码
<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>

vue3写法

Test01.vue

javascript 复制代码
<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>

子组件HeavyComp.vue

javascript 复制代码
<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>

useDefer.js

javascript 复制代码
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;
    }
}

结果:这里为了录制gif不卡顿,只渲染50个重组件

优点: 首屏渲染很快,后续的再依次加载

缺点: 快速滚动时,会出现短暂的白屏

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

使用虚拟列表的方式

通过这张图来表示虚拟列表,红框代表你的手机,黑条代表一条条数据

思路:我们只要知道手机屏幕 最多能放下几条数据 ,当下拉 滑动时,通过双指针 的方式截取相应的数据就可以了。

🚩 PS:为了防止滑动过快 导致的白屏 现象,我们可以使用预加载的方式多加载一些数据出来。

在Test02.vue

javascript 复制代码
<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>

结果:这里为了录制gif不卡顿,只渲染一万条

优点:页面上只渲染出可视区域的内容,非常丝滑,无白屏现象

相关推荐
Sam90294 分钟前
【Webpack--007】处理其他资源--视频音频
前端·webpack·音视频
Code成立5 分钟前
HTML5精粹练习第1章博客
前端·html·博客·html5
牵牛老人6 分钟前
Qt技巧(三)编辑框嵌入按钮,系统位数判断,判断某对象是否属于某种类,控件取句柄,支持4K,巧用QEventLoop,QWidget的窗体样式
开发语言·qt
Niu_brave10 分钟前
Python基础知识学习(2)
开发语言·python·学习
架构师ZYL17 分钟前
node.js+Koa框架+MySQL实现注册登录
前端·javascript·数据库·mysql·node.js
神仙别闹26 分钟前
基于C#+Mysql实现(界面)企业的设备管理系统
开发语言·mysql·c#
大柏怎么被偷了33 分钟前
【C++算法】位运算
开发语言·c++·算法
程序猿方梓燚35 分钟前
C/C++实现植物大战僵尸(PVZ)(打地鼠版)
c语言·开发语言·c++·算法·游戏
CPP_ZhouXuyang35 分钟前
C语言——模拟实现strcpy
c语言·开发语言·数据结构·算法·程序员创富
Zucker n41 分钟前
猫狗识别大模型——基于python语言
开发语言·python