This is the first day of my participation in the Gwen Challenge. For more details, see: “Gwen Challenge” 😛

Performance optimization is a big topic in itself, and it does not have a formula, it is best to specific analysis in a specific project, rather than to see an optimization technique must be used in the project, this article mainly discusses in the VUE coding stage there are common optimization means

Use the key 🔑

As mentioned in this article about the DIff algorithm of VUE, the key value can identify the virtual node when comparing the old and new virtual nodes. When updating child nodes, the list of the old virtual node and the node with the same as the new virtual node need to be updated. If you set a key value during the comparison process, the comparison will be much faster. For a list that is generated through a loop, you should add a stable and unique key to each list item so that as the list changes, as few elements are removed, added, or changed.

🥶 Use frozen objects

What is a frozen object? Frozen Object is through Object. Freeze (an Object) to freeze the Object, after freezing, the attribute of this Object cannot be modified to add, is immutable, array, of course, also can freeze, frozen after what operation can’t, don’t want to increase bowdlerize, because after freezing Object immutable, vue will optimize the frozen Object processing, Vue does not treat frozen objects as responsive. In a real project, we might be dealing with data that doesn’t change, it just needs to be rendered on the page, so it doesn’t need to be responsive. Using frozen objects reduces the time it takes for VUE to make objects responsive.

The downside is that if you want to modify the data in the object in the future, it will not render on the page because it is not reactive.

⏳ uses computed properties

If a certain data in the template will be used many times, and the data is obtained by calculation, using computational attributes as far as possible, as we all know, calculation of the attribute is a cache to calculate attribute functions depend on the data in the absence of change, repeatedly read cache data, computing the attribute function will not be executed repeatedly, but there are defects, just can’t pass parameters.

📜 Non-real-time binding form entry

When a form item is bound using the V-Model, when the user changes the state of the form item, the data changes as well, causing the VUE to rerender, which incurs some performance overhead.

In particular, when the user changes form items, the page has some animation going on, and since the JS execution thread and the browser render thread are mutually exclusive, the animation will end up stuttering.

We can solve the problem by using lazy or not using v-Model, but be aware that this can result in inconsistent data and form item values for a certain period of time.

A simple example: Insert a task into a list

When we use the V-Model for two-way binding, we do not add the lazy modifier, and then change the transition time to 5s


<template>
  <div id="app">
    <input
      type="text"
      placeholder="Have you finished your work for today?"
      v-model="message"
      @change="addContent"
    />
    <button @click="shuffle">Random sequence</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"."Reading"."Eat"."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: 0auto; {} *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 {
  /* Change the time here */
  transition: 5s;
}
.nums-leave-to {
  opacity: 0;
  transform: translateX(300px);
}
.nums-leave-active {
  position: absolute;
}
</style>

Copy the code

The effect is as follows:

We can clearly see that when adding a piece of content, the page renders slowly due to the constant input of content

Add the lazy modifier to the V-model and see if it makes a big difference 😂

🍭 use v-show instead of v-if

For elements whose display states change frequently, v-show can ensure the stability of the virtual DOM tree and avoid frequent addition and deletion of elements, especially when the node contains a large number of DOM elements.

🍄 Use delayed load (defer)

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

  • Excessive packing volume

    The large size of the package consumes a lot of transmission time, resulting in only a

    page before the completion of Js transmission, with no content to display.
  • Too much content to render immediately

    After the JS transfer is complete, the browser starts to execute JS to construct the page. However, there may be too many components to render in the beginning, which not only causes Js execution to take too long, but also causes the browser to render too many elements after execution, resulting in a blank screen

The packaging volume is too large to optimize the packaging volume, here will not say, mainly talk about the problem of excessive rendering content.

One possible solution is to delay loading components and allow them to be rendered one by one in the specified order.

Lazy loading is an idea that essentially renders content in batches using the requestAnimationFrame event, which can be implemented in a variety of ways.

🍉 keep alive – components

For more information about keep-alive, see this article: Explain what the keep-alive component does and how it works

🍍 optimizes using long lists

Sometimes we need to be displayed on the page very long list, this situation occurs mainly on the mobile end or background management page, often have a drop-down on the mobile end refreshes the content of the function, kept turning up, and after the loads more content, and this can result in the list there will be a lot of elements, which leads to pages of card, because the element is much later, Browser rendering also takes time, especially if some new elements are added, which triggers rearrangement and redrawing of the browser, so either memory usage or GPU rendering can cause some performance loss.

For example 🌰 :

Suppose we need to render 10,000 pieces of data in a long list of pages, 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>

Copy the code
/ / 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>

Copy the code

The effect is as follows:

Next, let’s take a look at the page loading performance analysis graph:

It is clear from the figure that zero execution of the script takes more than 6s, rendering takes nearly 1s, and CPU usage starts at 46MB up to 196MB

So how do you solve this problem? The general idea is as follows: make the page display only what we can see, and not what we can’t see. Then listen for changes in the scrollbar, and redisplay the visible area when the scrollbar changes.

Initial appearance:

When sliding a position:

We just look at the green border area. When we move one position, the data bar representing 1 disappears and the data bar representing 7 appears again. In fact, the position has changed, and 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>

Copy the code

ListItem. Vue components

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

Copy the code

RecycleScroller. Vue components

// 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: {
    // An array of data
    items: {
      type: Array.default: () = >[],},// The height of each column
    itemSize: {
      type: Number.default: 0,},keyField: {
      // In the items array given to me, which property of each object represents a unique and stable number
      type: String.default: "id",}},data() {
    return {
      pool: [].// Render pool, save the current need to render data
    };
  },
  mounted() {
    this.setPool();
  },
  computed: {
    totalSize() {
      return this.items.length * this.itemSize; // Total height, height of each element * number}},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 intercept
      let endIndex = Math.ceil((scrollTop + height) / this.itemSize);// Get the end of the data to intercept
      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>

Copy the code

The final result

Also rendering 10000 data, let’s take a look at the performance graph of this scheme:

From the figure, we can clearly see that the script took 335ms to execute, 6ms to render, and 14.3MB to 30MB of memory, which is a huge difference from the first one.

You don’t need to remember the code above, there is a plug-in for long list optimization called VUe-virtual-Scroller, link address

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

  1. Install NPM I vue-virtual-scroller

  2. Change the code, remove the RecycleScroller, import the newly installed components, don’t forget to import CSS file import ‘ue-virtual-scroller/dist/ UE – virtual-scroll.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 to also introduce 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>

Copy the code

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

😊, that’s all I have to say about duck in the comments section

I hope you can like 👍 to support oh ~ 😘, I will be more motivated 🤞