Optimization method of vue coding

Performance optimization itself is a big topic, and it doesn't have a formula. It's best to analyze it in a specific project, rather than see that an optimization technique must be used in the project. This article mainly talks about the common optimization methods in the vue coding stage

🔑 Use key

About key in this article Please explain the diff algorithm of vue It is said in the article that the key value can identify the virtual node when comparing the old and new virtual nodes. When updating the child nodes, it is necessary to update the old virtual node list with the same node as the new virtual node. If the key value is set in the comparison process, the comparison speed will be much faster. For the list generated through circulation, a stable and unique key should be added to each list item, which is conducive to deleting, adding and changing as few elements as possible when the list changes.

🥶 Use frozen objects

What is a frozen object? Freezing objects is actually through object Freeze (pass an object) freezes the object. After freezing, the properties of the object cannot be modified or added. It is immutable. Of course, the array can also be frozen. After freezing, no operation can be done. Don't think about adding, deleting or changing. Because the frozen object is immutable, vue will optimize the frozen object, and vue will not process the frozen object into a response. In the actual project development, we may process the data that will not change. It only needs to be rendered on the page, so it is not necessary for these data to become responsive. At this time, using frozen objects can reduce the time of vue turning objects into responsive processes.

Of course, it also has a disadvantage that if you want to modify the data in the object in the future, it will not be rendered on the page because it is not responsive.

⏳ Use calculated properties

If a data in the template will be used many times and the data is obtained through calculation, try to use the calculation attribute. We all know that the calculation attribute is cached. The data relied on by the calculation attribute function will read the cached data repeatedly without change, and the calculation attribute function will not be executed repeatedly, but it also has the defect that it cannot pass parameters.

📜 Non real time bound form items

When using v-model to bind a form item, when the user changes the state of the form item, it will also change the data, resulting in the re rendering of vue, which will bring some performance overhead.

Especially when the user changes the form item, some animations on the page are in progress. Because the JS execution thread and browser rendering thread are mutually exclusive, the animation will eventually get stuck.

We can solve the problem by using lazy or not using v-model, but we should note that this may lead to inconsistent values of data and form items in a certain period of time.

A simple example: insert a task into the list

When we directly use v-model for two-way binding, first do not add the lazy modifier, and then adjust the transition transition time to 5s

<template>
  <div id="app">
    <input
      type="text"
      placeholder="Have you finished today's task?"
      v-model="message"
      @change="addContent"
    />
    <button @click="shuffle">Random sorting</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)">complete</button>
      </li>
    </transition-group>
  </div>
</template>

<script>
export default {
  data() {
    return {
      likeList: ["Write code", "read a book", "having dinner", "Chasing girls"],
      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("Current task already exists, please re-enter!");
        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 {
  /* Just modify the time here */
  transition: 5s;
}
.nums-leave-to {
  opacity: 0;
  transform: translateX(300px);
}
.nums-leave-active {
  position: absolute;
}
</style>

The effect is as follows:

We can clearly see that when adding a content, the continuous input of content will slow down the rendering of the page

Then add the lazy modifier to the v-model and see if it is very different from not adding it 😂

🍭 Use v-show instead of v-if

For the elements that frequently switch the display state, using v-show can ensure the stability of the virtual dom tree and avoid frequent addition and deletion of elements, especially when the nodes contain a large number of dom elements.

🍄 Use defer red mount

Delayed loading is mainly used to solve the white screen problem. The white screen time of the home page is mainly affected by two factors:

  • Packing volume is too large

    The volume of the package is too large, which requires a lot of transmission time. As a result, there is only one < div > on the page before the Js transmission is completed, and there is no content to display.

  • There is too much to render immediately

    After the JS transmission is completed, the browser starts to execute JS to construct the page. However, there may be too many components to render at the beginning, which will not only lead to long execution time of JS, but also too many elements to be rendered by the browser after execution, resulting in white screen

If the packing volume is too large, you need to optimize the packing volume by yourself. I won't talk about it here. I mainly talk about the problem of too much rendering content.

A feasible way is to delay loading components and render them one by one in the specified order.

Delayed loading is an idea, which essentially uses the requestAnimationFrame event to render content in batches. Its specific implementation is diverse.

🍉 Keep alive component

About keep alive, you can see this article: Please explain the function and principle of keep alive component

🍍 Use long list optimization

Sometimes we need to display a very long list on the page, which mainly occurs in the pages managed by the mobile terminal or the background. The mobile terminal often has a function of refreshing the content from the drop-down, constantly turning up, and more content will be loaded after the end. This will lead to many elements in the list, resulting in the jamming of the page. After there are many elements, Browser rendering also takes time. In particular, the addition of some elements will also trigger the rearrangement and redrawing of the browser. Therefore, whether it is the occupation of memory or GPU rendering, it will bring some loss to the performance.

Take a chestnut 🌰:

Suppose we need to render 10000 pieces of data in the page long list. The code is as follows:

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

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

The effect is shown in the figure:

Next, let's take a look at the page loading performance analysis chart:

It can be clearly seen from the figure that the 0 execution of the script takes more than 6s, the rendering time is nearly 1s, and the CPU consumption is from 46MB to 196MB

How to solve this problem? The general idea is as follows: let the page only display what we can see and not what we can't see, and then monitor the change of the scroll bar. When the scroll bar changes, redisplay the visible area. Simply draw a picture:

Initial appearance:

When sliding a position:

We only observe the green border area. When we move a position, the data bar representing 1 disappears and the data bar representing 7 appears again. In fact, the position has changed. This is the main idea of implementation.

Code implementation:

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 component

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

// 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: {
    //Array of data
    items: {
      type: Array,
      default: () => [],
    },
    //Height of each piece of data
    itemSize: {
      type: Number,
      default: 0,
    },
    keyField: {
      //In the items array given to me, which attribute of each object represents a unique and stable number
      type: String,
      default: "id",
    },
  },
  data() {
    return {
      pool: [], //Rendering pool, which saves the data currently to be rendered
    };
  },
  mounted() {
    this.setPool();
  },
  computed: {
    totalSize() {
      return this.items.length * this.itemSize; //Total height, height of each element * quantity
    },
  },
  methods: {
    //Get the data to be rendered and add it to the pool array
    setPool() {
      let scrollTop = this.$refs.container.scrollTop;
      let height = this.$refs.container.clientHeight;
      let startIndex = Math.floor(scrollTop / this.itemSize);//Get the starting point of the data to be intercepted
      let endIndex = Math.ceil((scrollTop + height) / this.itemSize);//Get the end point of the data to be intercepted
      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>

Final effect

Similarly, 10000 pieces of data are rendered. Let's take a look at the performance diagram of this scheme:

It can be clearly seen from the figure that the script took 335ms to execute, 6ms to render, and 14.3MB to 30MB of memory, which is very different from the first performance chart.

There is no need to remember the above code. There is a plug-in called Vue virtual scroller for long list optimization, Link address

Next, we use this plug-in in the project:

  1. Install NPM I Vue virtual scroller

  2. Modify the code, remove the original RecycleScroller, import the newly installed components, and don't forget to import the CSS file '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';//Remember the introduction of 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>

After importing the plug-in, the effect is the same.

😊 Well, the above is my share. Welcome to discuss duck in the comment area

I hope you guys like it 👍 Support 😘, I'll be more motivated 🤞

Keywords: Front-end Vue

Added by Cyberspace on Tue, 01 Feb 2022 20:31:41 +0200