前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue编码之长列表优化

vue编码之长列表优化

作者头像
程序员法医
发布2022-08-11 16:17:38
3160
发布2022-08-11 16:17:38
举报
文章被收录于专栏:vue全家桶vue全家桶

今天主要想跟大家聊聊长列表优化,有的时候我们需要在页面上显示特别长的列表,这种情况主要发生在移动端或者后台管理的页面中,在移动端往往有个下拉刷新内容的功能,不停地往上翻,到底部后会加载更多内容,这样会导致列表中会有很多元素,从而导致页面的卡顿,由于元素多了以后,浏览器渲染也需要时间,特别是新增了一些元素,也会触发浏览器的重排重绘,因此无论是内存的占用或者GPU的渲染都会给性能带来一些损耗。

举个栗子🌰:

假设我们需要在页面长列表中渲染10000条数据,代码如下:

代码语言:javascript
复制
//APP.vue

<template>
  <div class="app">
    <div class="scroller">
      <Listltem v-for="item in item" :key="item.id" :item="item" />   
    </div>
  </div>
</template>

<script>
import Listltem from "./components/Listltem.vue";
var item = [];
for (var i = 0; i < 10000;i++) {
  item.push({
    id:i + 1,
    count:i + 1,
  })
}
export default {
 components:{
   Listltem,
 },
  data() {
    return {
      item,
    };
  },
 
};
</script>

<style lang="less" scoped>

</style>

代码语言:javascript
复制
//组件Listltem.vue

<template>
  <div class="list-container">
    <span>id{{item.id}}</span>
    <span>name{{item.count}}</span>
    <span>age{{item.count}}</span>


  </div>
</template>

<script>
export default {
  props: {
    item:Object,
  },
  data() {
    return {};
  },

};
</script>

<style lang="less" scoped>
  .list-container{
    margin: 0 auto;
    width: 500px;
    text-align: center;
    height: 54px;
    padding: 1em;
    display: grid;
    box-sizing: border-box;
    grid-template-columns: repeat(3,1fr);
    box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
  }
</style>

效果如图:

GIF 2021-5-30 14-44-42.gif

接下来,我们看下页面加载性能分析图:

image.png

从图中可以清楚的看到脚本的0执行占用6s多,渲染用时将近1s,而且CPU占用从开始的46MB一直到196MB

那怎么解决这个问题呢?总体思路是这样的:让页面只显示我们能看到的东西,看不到的东西不显示,然后监听滚动条的变化,当滚动条变化的时候重新显示可见区域就完事了,简单画个图:

初始样子:

长列表优化.png

当滑动了一个位置:

长列表优化.png

我们只观察绿色边框区域就行了,当移动一个位置后,表示1的数据条消失了,表示7的数据条又出现了,其实只是位置发生了变化,这就是主要实现的思路。

代码实现:

APP.vue

代码语言:javascript
复制
//APP.vue
<template>
  <div id="app">
    <RecycleScroller 
      :items="items" 
      :itemSize="54" 
      class="scroller"
      v-slot="{item}"
      >
        <ListItem :item="item" />
    </RecycleScroller>
  </div>
</template>

<script>
import ListItem from "./components/Listltem.vue";
import RecycleScroller from "./components/RecycleScroller.vue";
var items = [];
for (var i = 0; i < 10000; i++) {
  items.push({
    id: i + 1,
    count: i + 1,
  });
}
export default {
  name: "App",
  components: {
    RecycleScroller,
    ListItem,
  },
  data() {
    return {
      items,
    };
  },
};
</script>

<style>
#app {
  width: 100%;
  margin: 0 auto;
}
.scroller {
  width: 500px;
  height: 500px;
  margin: 0 auto;
}
</style>

ListItem.vue 组件

代码语言:javascript
复制
//ListItem.vue
<template>
  <div class="list-container">
    <span>id{{item.id}}</span>
    <span>name{{item.count}}</span>
    <span>age{{item.count}}</span>
  </div>
</template>

<script>
export default {
  props: {
    item:Object,
  },
};
</script>

<style  >
  .list-container{
    margin: 0 auto;
    width: 500px;
    text-align: center;
    height: 54px;
    padding: 1em;
    display: grid;
    box-sizing: border-box;
    grid-template-columns: repeat(3,1fr);
    box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
  }
</style>

RecycleScroller.vue 组件

代码语言:javascript
复制
// RecycleScroller.vue
<template>
  <div class="recycle-scroller-container" @scroll="setPool" ref="container">
    <div class="recycle-scroller-wrapper" :style="{ height: `${totalSize}px` }">
      <div
        class="recycle-scroller-item"
        v-for="poolItem in pool"
        :key="poolItem.item[keyField]"
        :style="{
          transform: `translateY(${poolItem.position}px)`,
        }"
      >
        <slot :item="poolItem.item"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    //数据的数组
    items: {
      type: Array,
      default: () => [],
    },
    //每一条数据的高度
    itemSize: {
      type: Number,
      default: 0,
    },
    keyField: {
      //给我的items数组中,每个对象哪个属性代表唯一且稳定的编号
      type: String,
      default: "id",
    },
  },
  data() {
    return {
      pool: [], //渲染池,保存当前需要渲染的数据
    };
  },
  mounted() {
    this.setPool();
  },
  computed: {
    totalSize() {
      return this.items.length * this.itemSize; //总高度,每一个元素的高度 * 数量
    },
  },
  methods: {
    //拿到需要被渲染的数据添加到 pool数组中
    setPool() {
      let scrollTop = this.$refs.container.scrollTop;
      let height = this.$refs.container.clientHeight;
      let startIndex = Math.floor(scrollTop / this.itemSize);//获取到要截取数据的起点
      let endIndex = Math.ceil((scrollTop + height) / this.itemSize);//获取到要截取数据的终点
      let scrollPos = startIndex * this.itemSize;
      this.pool = this.items.slice(startIndex, endIndex).map((it, i) => ({
        item: it,
        position: scrollPos + i * this.itemSize,
      }));
    },
  },
};
</script>

<style>
.recycle-scroller-container {
  overflow: auto;
}
.recycle-scroller-wrapper {
  position: relative;
}
.recycle-scroller-item {
  position: absolute;
  width: 100%;
  left: 0;
  top: 0;
}
</style>

最终效果:

GIF 2021-5-31 19-53-44456.gif

同样是渲染10000条数据,我们来看下这种方案性能图:

image.png

从图中可以清楚的看到脚本的执行用了335ms,渲染用时6ms,内存占用14.3MB到30MB,跟第一次性能图可谓天差地别。

上面的代码大家没必要记,关于长列表优化这块是有一个插件的,名字叫vue-virtual-scroller,链接地址

接下来,我们在项目中使用这个插件:

  1. 安装 npm i vue-virtual-scroller
  2. 修改代码,将原先RecycleScroller移除,导入新安装的组件,别忘了还要引入css文件import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

APP.vue

代码语言:javascript
复制
//APP.vue
<template>
  <div id="app">
    <RecycleScroller 
      :items="items" 
      :itemSize="54" 
      class="scroller"
      v-slot="{item}"
      >
        <ListItem :item="item" />
    </RecycleScroller>
  </div>
</template>

<script>
import ListItem from "./components/Listltem.vue";
import {RecycleScroller} from "vue-virtual-scroller";
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';//记得还有引入css
// import RecycleScroller from "./components/RecycleScroller.vue";
var items = [];
for (var i = 0; i < 10000; i++) {
  items.push({
    id: i + 1,
    count: i + 1,
  });
}
export default {
  name: "App",
  components: {
    RecycleScroller,
    ListItem,
  },
  data() {
    return {
      items,
    };
  },
};
</script>

<style>
#app {
  width: 100%;
  margin: 0 auto;
}
.scroller {
  width: 500px;
  height: 500px;
  margin: 0 auto;
}
</style>

导入插件后,效果是一样的。

😊 好了, 以上就是我的分享,欢迎大家在评论区讨论鸭~

希望小伙伴们点赞 👍 支持一下哦~ 😘,我会更有动力的 🤞

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端猎手 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档