Code by no means can run, but nonsense just say a sentence: code words are not easy to find 👍, 🙇🙇🙇.

The demo code is written using Vue3 + TS + Vite, but also lists optimization tips for Vue2. If an optimization only works for Vue3 or Vue2, I’ll highlight it in the title.

Code optimization

Key is used in V-for

When using V-for to update a list of rendered elements, the default is in-place reuse. When the list data is modified, it will determine whether a value is changed based on the key value. If it is changed, it will re-render the item. Otherwise, it will reuse the previous element.

Precautions for using keys:

  • Do not use things that may be repeated or variedkeyValue (the console will also alert you)
  • Don’t use arraysindexAs akeyValue, because if you insert an element into an array, the index element behind it will change.
  • If the array does not have a unique key value available, consider adding a key field to the array with a value of Symbol() to ensure it is unique.

V-if/V-else -if/ V-else uses key

Probably a lot of people miss this point

Reason: By default, Vue updates the DOM as efficiently as possible. This means that when it switches between elements of the same type, it fixes existing elements, rather than removing the old one and adding a new one in the same place. If elements that are not identical are identified as identical, unexpected side effects can occur.

If there’s only one V-if, no V-else or v-if-else, there’s no need to add a key

V -if/ V -else-if/ V -else keys are relatively simple, we can write a fixed string or array

  <transition>
    <button 
      v-if="isEditing"
      v-on:click="isEditing = false"
    >
      Save
    </button>
    <button 
      v-else 
      v-on:click="isEditing = true"
    >
      Edit
    </button>
  </transition>
Copy the code
.v-enter-active..v-leave-active {
  transition: all 1s;
}
.v-enter..v-leave-to {
  opacity: 0;
  transform: translateY(30px);
}
.v-leave-active {
  position: absolute;
}
Copy the code

For example, in the code above, you can see that although you add a transition effect to the button, you can’t trigger a transition without adding a key switch

Do not use v-for and V-if together (Vue2)

This optimization technique is limited to Vue2, where the v-for and V-IF priorities are adjusted in Vue3

Everybody knows that

Never use v-if and V-for on the same element. Lead to the Vue2. X Style Guide

The reason is that v-for has a higher priority than V-if, so when they are used on the same tag, each render will loop through the conditions first

Note: V-if has a higher priority than V-for in Vue3, so when v-for and V-if are used together, the effect is similar to that of v-if in Vue2

For example, the following code is not recommended in Vue2 and will be warned by Vue

<ul>
  <li v-for="user in users" v-if="user.active">
    {{ user.name }}
  </li>
</ul>
Copy the code

We should try to move v-If to a higher level or use computed properties to process data

<ul v-if="active">
  <li v-for="user in users">
    {{ user.name }}
  </li>
</ul>
Copy the code

If you don’t want the contents of the loop to have an unnecessary parent container, you can choose to use template as its parent element. Template is not rendered as a DOM node by the browser

If I want to judge the contents of each item in a traversal object and choose the data to render, I can use computed to filter the traversal object

// js
let usersActive = computed(()=>users.filter(user => user.active))

// template
<ul>
    <li v-for="user in usersActive">
      {{ user.name }}
    </li>
</ul>
Copy the code

Reasonable choice of V-if and V-show

The difference between V-if and V-show is very familiar; V-if controls the display and hiding of elements by directly manipulating DOM deletions and additions; V-show is familiar with controlling the display CSS of the DOM to control the display and hiding of elements

Because the performance of DOM add/remove operations is much lower than the CSS properties that operate on the DOM

So when elements need to show/hide changes frequently, we use V-show to improve performance.

Removing the DOM by v-if saves the browser some of the DOM resources needed to render it when the element does not need to be shown/hidden frequently

Use simple computed properties

Complex computed properties should be split into as many simpler properties as possible.

  • Easy to test

    When each evaluated property contains a very simple and rarely dependent expression, it is much easier to write tests to ensure that they work correctly.

  • Easy to read

    Simplifying the calculation of attributes requires that you give each value a descriptive name, even if it is not reusable. This makes it easier for other developers (and you in the future) to focus on the code they care about and figure out what’s going on.

  • Better “Embrace change”

    Any value that can be named can be used on the view. For example, we might want to display a message telling the user how much money they have saved; Taxes may also be calculated, but they may be presented separately rather than as part of the total price.

    Small, focused computational attributes reduce the hypothetical constraints on how information can be used, so there is less refactoring when requirements change.

Lead to the Vue2 Style Guide

Computed, as you are familiar with, recalculates when the responsive data delivery that its expressions depend on changes. If we write a more complex expression in a calculated property, the amount of reactive data it depends on becomes arbitrarily larger. The entire expression needs to be recalculated when any of these dependencies change

let price = computed(() = >{
  let basePrice = manufactureCost / (1 - profitMargin)
  return (
      basePrice -
      basePrice * (discountPercent || 0))})Copy the code

If any of manufactureCost, profitMargin, or discountPercent changes, the whole price is recalculated.

But if we change it to the following

let basePrice = computed(() = > manufactureCost / (1 - profitMargin))
let discount = computed(() = > basePrice * (discountPercent || 0))
let finalPrice = computed(() = > basePrice - discount)
Copy the code

If discountPercent changes, only discount and finalPrice are recalculated, but basePrice is not recalculated due to the caching nature of computed data

Functional Components (Vue2)

Note that this was only used as a means of optimization in Vue2, and in 3.x the performance difference between statically and functional components has been greatly reduced and is negligible in most use cases. Therefore, the migration path for developers using functional on SFCs is to remove the attribute and rename all references to props to $props and attrs to $attrs.

Before optimization

<template> 
    <div class="cell"> 
        <div v-if="value" class="on"></div> 
        <section v-else class="off"></section> 
    </div> 
</template> 

<script> 
export default { 
    props: ['value'],}</script>
Copy the code

The optimized

<template functional> 
    <div class="cell"> 
        <div v-if="props.value" class="on"></div> 
        <section v-else class="off"></section> 
    </div> 
</template> 

<script> 
export default { 
    props: ['value'],}</script>
Copy the code
  • No this (no instance)
  • There is no responsive data

Break up the component

What? You wrote a VUE file with over a thousand lines of code? 🤔

Splitting components properly not only optimizes performance, but also makes your code more legible. The single function principle

Derived from slides.com/akryum/vuec…

Before optimization

<template>
  <div :style="{ opacity: number / 300 }">
    <div>{{ heavy() }}</div>
  </div>
</template>

<script>
export default {
  props: ['number'].methods: {
    heavy () { /* HEAVY TASK */}}}</script>
Copy the code

The optimized

<template>
  <div :style="{ opacity: number / 300 }">
    <ChildComp/>
  </div>
</template>

<script>
export default {
  props: ['number'].components: {
    ChildComp: {
      methods: {
        heavy () { /* HEAVY TASK */ }
      },
      render (h) {
        return h('div'.this.heavy())
      }
    }
  }
}
</script>
Copy the code

Since Vue updates are component-grained, while every frame is re-rendered by data modification of the parent component, ChildComp does not re-render because it does not have any internal responsive data changes. So the optimized components do not perform time-consuming tasks on every render

Using local variables

Before optimization

<template>
  <div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>

<script>
import { heavy } from '@/utils'

export default {
  props: ['start'].computed: {
    base () { return 42 },
    result () {
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += heavy(this.base)
      }
      return result
    }
  }
}
</script>
Copy the code

The optimized

<template>
  <div :style="{ opacity: start / 300 }">
    {{ result }}</div>
</template>

<script>
import { heavy } from '@/utils'

export default {
  props: ['start'].computed: {
    base () { return 42 },
    result () {
      const base = this.base
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += heavy(base)
      }
      return result
    }
  }
}
</script>
Copy the code

The main difference here is the implementation difference of the calculation attribute result of the component before and after optimization. The component before optimization accesses this.base several times in the calculation process, while the optimized component will use the local variable base before calculation, cache this.base, and then directly access base.

The reason why this difference makes a performance difference is that every time you access this.base, since this.base is a responsive object, it fires its getter and executes the dependency collection logic. Similar logic is performed too often, as in the example where hundreds of loops update hundreds of components, each triggering computed recalculation, and then performing the dependent collection-related logic multiple times, and performance naturally degrades.

This. Base performs a dependency collection once and returns the result of its getter to the local variable base. The getter is not triggered when the base is accessed again, and the dependency collection logic is not used.

Here are nine performance optimization tips for vue.js

Use the KeepAlive

Keep-alive can be used to cache components that are expensive to render and need to be switched frequently

After keep-alive is used, the vNode and DOM of the component wrapped by keep-alive will be cached after the first rendering. Then, when rendering the component again, the corresponding VNode and DOM will be directly retrieved from the cache and rendered. There is no need to go through another component initialization, render and patch etc. series of processes, reduce script execution time, better performance.

Note: Abusing keep-Alive will only make your application more sluggish because it will take up a lot of memory for a long time

Destruction of events

When a component is destroyed, we should remove global events and timers added to the component to prevent memory leaks

Vue3’s hooks allow us to write event declarations and destruction together, making them more readable

function scrollFun(){ / *... * /}
document.addEventListener("scroll", scrollFun)

onBeforeUnmount(() = >{
  document.removeEventListener("scroll", scrollFun)
})
Copy the code

Vue2 can still do this with $once, of course you can also destroy events in optionsAPI beforeDestroy, but I prefer the former as the latter makes the code for the same function more scattered

function scrollFun(){ / *... * /}
document.addEventListener("scroll", scrollFun)

this.$once('hook:beforeDestroy'.() = >{
  document.addEventListener("scroll", scrollFun)
})
Copy the code
function scrollFun(){ / *... * /}

export default {
  created() {
    document.addEventListener("scroll", scrollFun)
  },
  beforeDestroy(){
    document.addEventListener("scroll", scrollFun)
  }
}
Copy the code

Image to load

Image lazy loading: The vue-Lazyload plugin provides a convenient v-lazy loading command for clearing pages with many images and not all images are displayed on a single screen

However, not all images are suitable for lazy loading. For example, it is recommended to use image preloading technology, such as banner, album, etc., to download the first and last images of the current display first.

Use reasonable data processing algorithms

This comparison tests the strength of data structures and algorithms

An example is a method that converts an array to a multilevel structure

/** * array to tree, time complexity O(n) *@param * the list array@param IdKey Element ID key *@param ParIdKey The element's parent ID key *@param ParId Specifies the parent ID * of the primary root node@return {} []* /
function listToTree (list,idKey,parIdKey,parId) {
    let map = {};
    let result = [];
    let len = list.length;

    / / build the map
    for (let i = 0; i < len; i++) {
        // Convert the data in the array to a key-value pair structure (array and obj reference each other, which is the key of the algorithm implementation)
        map[list[i][idKey]] = list[i];
    }

    // Build the tree array
    for(let i=0; i < len; i++) {
        let itemParId = list[i][parIdKey];
        // Top-level node
        if(itemParId === parId) {
            result.push(list[i]);
            continue;
        }
        // Orphan node, discard (no parent exists)
        if(! map[itemParId]){continue;
        }
        // Insert the current node into the children of the parent node.
        if(map[itemParId].children) {
            map[itemParId].children.push(list[i]);
        } else{ map[itemParId].children = [list[i]]; }}return result;
}
Copy the code

other

In addition to the above methods there are many optimization techniques, but I don’t use them very often in projects 🤣

  • Freeze objects (to prevent unresponsive data from becoming responsive)
  • Long list render – Batch render
  • Long List Rendering – Dynamic Rendering (VUe-virtual-Scroller)
  • .

First screen/volume optimization

In my project, I mainly have the following optimization directions for the first screen

  • volume
  • The code segment
  • network

Volume optimization

  • Compress your code: WebPack and Vite’s production packaging will compress your code by default. This usually requires no special handling, and Webpack can also be manually implemented through the corresponding compression plug-in

  • Disable source-map: You can check to see if there is a.map file in your package. If there is, you can turn off code mapping by setting source-map to false or null (this takes up a lot of space).

  • Package enable GIZP compression: this requires the server to also enable GIZP transmission, otherwise enable also no use (Webpack has the corresponding gzip compression plug-in, not too version of the Webpack compression plug-in may be different, it is recommended to check the official website)

The code segment

The code split function splits the package into smaller pieces that depend on the esModule. So when you use the import() function to import a file or dependency, the file or dependency is packaged separately as a small artifact. Routing lazy loading and asynchronous components use this principle.

  • Route lazy loading
  • Asynchronous components

I generally don’t use on-demand components for UI libraries, preferring the CDN approach for optimization.

network

CDN: First of all, the above mentioned CDN import, the development phase uses the local library, by configuring external extensions (Externals) packaging to eliminate these dependencies. They are then imported via CDN in an HTML file

Server Push: HTTP2 is relatively mature; With the INTRODUCTION of the CDN above, we can use HTTP2’s Server Push feature on the web site to get the browser to pre-load these CDN and other files.

Enable Gzip: This has been described above, the principle is that when both client and server support gzip transfer, the server will preferentially send the gZIP-compressed file, and then the client will receive it for decompression.

Enable caching: I usually use a negotiated cache, but this is not suitable for all cases, such as Server Push files where you can’t arbitrarily change the file name. So I will also generally produce the main file name fixed