性能优化自身就是一个很大的话题,并且它没有一个定式,最好是在具体的我的项目中具体分析,而不是说看到一个优化技巧肯定要用在我的项目当中,这篇文章次要聊聊在vue编码阶段有哪些常见的优化伎俩

应用 key

对于key在这篇 请论述vue的diff算法文章有说到,key值在比照新旧虚构节点时能够辨识虚构节点,在更新子节点的时候,须要将旧虚构节点列表与新虚构节点雷同的节点进行更新。如果在比照过程中设置了key值,那么比照的速度就会快很多。对于通过循环生成的列表,应该给每个列表项增加一个稳固且惟一的key,这样有利于在列表发生变化时,尽量少删除、新增、改变元素。

应用解冻对象

什么是解冻对象?解冻对象其实就是通过Object.freeze(传一个对象)将对象解冻,解冻之后,这个对象的属性就不能批改增加了,是不可变的,当然数组也能解冻,解冻后什么操作都不行,增删改就不要想了,因为解冻对象后不可变,vue会对解冻对象进行优化解决,vue不会将解冻的对象解决成响应式。咱们在理论我的项目开发中可能会解决不会扭转的数据,它只须要渲染到页面上就行了,所以这些数据是没必要变成响应式的,这时应用解冻对象能够缩小vue将对象变成响应式过程这个工夫。

当然它也有一个弊病,就是未来想批改对象中的数据,因为不是响应式的,所以也不会渲染在页面上。

⏳ 应用计算属性

如果模板中某个数据会应用屡次,并且该数据是通过计算失去的,尽量应用计算属性,咱们都晓得计算属性是有缓存的,计算属性函数依赖的数据在没有发生变化的状况下,会重复读取缓存数据,而计算属性函数并不会重复执行,但也有缺点,就是不能传参数。

非实时绑定的表单项

当应用v-model绑定一个表单项时,当用户扭转表单项的状态时,也会随之扭转数据,从而导致vue产生从新渲染(rerender),这会带来一些性能的开销。

特地是当用户扭转表单项时,页面有一些动画正在进行中,因为JS执行线程和浏览器渲染线程是互斥的,最终会导致动画呈现卡顿。

咱们能够通过应用lazy或不应用v-model的形式解决问题,但要留神,这样可能导致在某个时间段内数据和表单项的值不统一。

一个简略例子: 插入一个工作到列表中

当咱们间接应用v-model进行双向绑定,先不加lazy修饰符,而后将transition过渡工夫调整为5s

<template>  <div id="app">    <input      type="text"      placeholder="明天的工作实现了吗?"      v-model="message"      @change="addContent"    />    <button @click="shuffle">随机排序</button>    <transition-group name="nums" tag="ul" class="box">      <li v-for="(item, i) in likeList" :key="item" class="task">        <span>{{ item }}</span>        <button @click="deleteContent(i)">实现</button>      </li>    </transition-group>  </div></template><script>export default {  data() {    return {      likeList: ["写代码", "看书", "吃饭", "追女孩子"],      message: "",    };  },  methods: {    deleteContent(i) {      this.likeList.splice(i, 1);    },    shuffle() {      this.likeList.sort(() => Math.random() - 0.5);    },    addContent() {      if (this.likeList.includes(this.message.replace(/\s+/g, ""))) {        alert("当前任务已存在,请从新输出!");        this.message = "";        return;      }      this.likeList.unshift(this.message.replace(/\s+/g, ""));      this.message = "";    },  },};</script><style>#app {  width: 500px;  margin: 0 auto;}* {  list-style: none;}input {  width: 600px;  height: 40px;  font-size: 24px;  border: none;  outline: none;  border-style: solid;  border-color: #ddd;  background: paleturquoise;  margin-bottom: 15px;  text-indent: 1em;}.box {  width: 500px;  padding: 0 20px;  margin-bottom: 200px;}.task {  width: 100%;  height: 50px;  line-height: 50px;  display: flex;  align-items: center;  justify-content: space-between;  border-bottom: 1px solid #ddd;}.task button {  width: 70px;  height: 30px;}.nums-enter {  opacity: 0;  transform: translateX(-300px);}.nums-enter-active,.nums-leave-active,.nums-move {  /* 批改这里的工夫即可 */  transition: 5s;}.nums-leave-to {  opacity: 0;  transform: translateX(300px);}.nums-leave-active {  position: absolute;}</style>

成果如下:

咱们能够很显著看到当增加一项内容的时候,不停的输出内容会导致页面渲染变慢

而后给v-model加上lazy修饰符再看看,是不是跟不加有很大区别

应用v-show代替v-if

对于频繁切换显示状态的元素,应用v-show能够保障虚构dom树的稳固,防止频繁的新增和删除元素,尤其是当外部蕴含大量的dom元素的节点。

应用提早装载(defer)

应用提早装载次要解决白屏问题,首页白屏工夫次要受两个因素的影响:

  • 打包体积过大

    包的体积过大须要耗费大量的传输工夫,导致Js传输实现前页面只有一个<div>,没有能够显示的内容。

    • 须要立刻渲染的内容过多

    JS传输实现后,浏览器开始执行JS结构页面。然而可能一开始要渲染的组件太多了,不仅会导致Js执行工夫很长,而且执行完后浏览器要渲染的元素过多,从而导致白屏

打包体积过大须要自行优化打包体积,这里就不说了,次要聊聊渲染内容过多的问题。

一个可行的方法就是提早装载组件,让组件依照指定的先后顺序顺次一个一个渲染进去。

提早装载是一个思路,实质上就是利用requestAnimationFrame 事件分批渲染内容,它的具体实现多种多样。

keep-alive组件

对于keep-alive 能够看看这篇文章:请论述keep-alive组件的作用和原理

应用长列表优化

有的时候咱们须要在页面上显示特地长的列表,这种状况次要产生在挪动端或者后盾治理的页面中,在挪动端往往有个下拉刷新内容的性能,不停地往上翻,到底后会加载更多内容,这样会导致列表中会有很多元素,从而导致页面的卡顿,因为元素多了当前,浏览器渲染也须要工夫,特地是新增了一些元素,也会触发浏览器的重排重绘,因而无论是内存的占用或者GPU的渲染都会给性能带来一些损耗。

举个栗子:

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

//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>
//组件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>

成果如图:

接下来,咱们看下页面加载性能剖析图:

从图中能够分明的看到脚本的0执行占用6s多,渲染用时将近1s,而且CPU占用从开始的46MB始终到196MB

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

初始样子:

当滑动了一个地位

咱们只察看绿色边框区域就行了,当挪动一个地位后,示意1的数据条隐没了,示意7的数据条又呈现了,其实只是地位产生了变动,这就是次要实现的思路。

代码实现:

APP.vue

//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 组件

//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 组件

// 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>

最终成果

同样是渲染10000条数据,咱们来看下这种计划性能图:

从图中能够分明的看到脚本的执行用了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

//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>

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

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

心愿小伙伴们点赞 反对一下哦~ ,我会更有能源的