SoFunction
Updated on 2025-04-11

22 Vue optimization tips (project practical)

The demo code is written in Vue3 + ts + Vite, but it also lists optimization tips for Vue2. If an optimization only works for Vue3 or Vue2, I will tag it in the title.

Code optimization

Use key in v-for

When using v-for to update the rendered element list, the in-place multiplexing strategy is used by default; when the list data is modified, it will determine whether a certain value is modified based on the key value. If it is modified, this item will be re-rendered, otherwise the previous element will be reused;

Notes on using keys:

  • Do not use key values ​​that may be repeated or may be changed (the console will also give a reminder)
  • Do not use the index of the array as the key value, because if an element is inserted into the array, the index of the following element will change.
  • If there is no unique key value in the array available, consider adding a key field to it, which is Symbol() to ensure uniqueness.

Use key in v-if/v-else-if/v-else

Many people may ignore this point

Cause: By default, Vue updates the DOM as efficiently as possible. This means that when it switches between elements of the same type, it patches up the existing element instead of removing the old element and adding a new element at the same location. If an element that is otherwise different is identified as the same, unexpected side effects will occur.

If there is only one v-if, no v-else or v-if-else, there is no need to add key

Compared with the key of v-for, the key in v-if/v-else-if/v-else is relatively simple. We can directly write to 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>
.v-enter-active, .v-leave-active {
  transition: all 1s;
}
.v-enter, .v-leave-to {
  opacity: 0;
  transform: translateY(30px);
}
.v-leave-active {
  position: absolute;
}

For example, for the above code, you will find that although the transition effect is added to the button, the transition cannot be triggered without adding the key switch.
v-for and v-if should not be used together (Vue2)

This optimization technique is limited to Vue2, Vue3 priorities for v-for and v-if

Everyone knows this

Never use v-if and v-for on the same element at the same time. Introduce toStyle Guide

The reason is that v-for has higher priority than v-if, so when they are used on the same label, each render will be looped first and then conditional judgment will be made.

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

For example, the following code is not recommended in Vue2, Vue will also give corresponding warnings

<ul>
  <li v-for="user in users" v-if="">
    {{  }}
  </li>
</ul>

We should try to move v-if to the previous level or use the computed attribute to process the data

<ul v-if="active">
  <li v-for="user in users">
    {{  }}
  </li>
</ul>

If you don't want to add an unnecessary upper container to the loop content, then you can choose to use template as its parent element, and template will not be rendered as a DOM node by the browser.
If I want to judge the content of each item in the traversal object to select the rendered data, I can use computered to filter the traversal object

// js
let usersActive = computed(()=>(user => ))

// template
<ul>
    <li v-for="user in usersActive">
      {{  }}
    </li>
</ul>

Reasonable choices of v-if and v-show

Everyone is very familiar with the difference between v-if and v-show; v-if controls the display and hiding of elements by directly operating the deletion and addition of DOM; v-show controls the display and hiding of elements by controlling the display and hiding of DOM by controlling the display and hiding of DOM
Because the operation performance of adding/removing DOM is much lower than that of the CSS properties of the operating DOM
So when elements need frequent display/hide changes, we use v-show to improve performance.
When elements do not need to be frequently displayed/hide changes, we remove DOM through v-if to save some of the resources required by the browser to render this DOM.

Use simple calculation of properties

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

Easy to test
When each computed property contains a very simple and seldom-dependent expression, it is easier to write a test to make sure it works correctly.

Easy to read
Simplified computational properties require you to 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 may be used on the view. For example, we might intend to show a message telling the user how much they have saved; we might also intend to calculate taxes, but we might conduct cash instead of as part of the total price.
Small, focused computing attributes reduce the assumptional limitations when using information, so there is no need to reconstruct so much when changing requirements.

Introduce toVue2 Style Guide

Computed is very familiar to everyone, it will recalculate when the responsive data dependent on in its expression sends changes. If we write more complex expressions in a computed property, the responsive data it depends on arbitrarily becomes more responsive. The entire expression needs to be recalculated when any of the dependencies change

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

The entire price will be recalculated when any of the manufacturerCost, profitMargin, and discountPercent changes.
But if we change it like this

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

If only discount and finalPrice are recalculated when discountPercent change, basePrice will not be recalculated due to the cache nature of computed

functional functional component (Vue2)

Note that this is only used as an optimization method in Vue2, where the performance differences between stateful and functional components have been greatly reduced and are trivial in most use cases. Therefore, the migration path for developers using functional on SFCs is to delete 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>

After optimization

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

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

  • No this (no instance)
  • No responsive data

Split components

What? A vue file you wrote has more than a thousand lines of code? 🤔
A reasonable splitting of components not only optimizes performance, but also makes the code clearer and more readable. The principle of single function

From/akryum/vueconfus-2019#/4/0/3

Before optimization

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

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

After optimization

<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', ())
      }
    }
  }
}
</script>

Since Vue's update is component granular, although each frame is modified through data, ChildComp will not re-render because there is no responsive data changes inside it. Therefore, the optimized component will not perform time-consuming tasks every time it renders

Use 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 = 
      for (let i = 0; i < 1000; i++) {
        result += heavy()
      }
      return result
    }
  }
}
</script>

After optimization

<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 = 
      let result = 
      for (let i = 0; i < 1000; i++) {
        result += heavy(base)
      }
      return result
    }
  }
}
</script>

This is mainly the implementation difference of the calculation attribute result of the components before and after optimization. The components before optimization are accessed many times during the calculation process, while the optimized components will first use the local variable base and cache before calculation, and then directly access the base.

So why does this difference cause performance differences? The reason is that every time you access , since it is a responsive object, its getter will be triggered, and then it will execute dependency collection related logical code. There are many similar logics, like the example, hundreds of components are updated in hundreds of loops, each component triggers a recalculation, and then performs multiple dependency collection related logic, so the performance will naturally decline.

From the demand point of view, it is enough to perform a dependency collection once. Return its getter evaluation result to the local variable base. When you access the base again, the getter will not be triggered, nor will it follow the logic of dependency collection, and the performance will naturally be improved.

Introduce toRevealing Nine Performance Optimization Tips

Use KeepAlive

When some components with relatively high rendering costs need to be switched frequently, keep-alive can be used to cache this component
After using keep-alive, the vnode and DOM of the component wrapped in keep-alive will be cached after the first rendering. Then, the next time the component is rendered again, the corresponding vnode and DOM are directly obtained from the cache, and then render. There is no need to go through a series of processes such as component initialization, rendering and patch, which reduces the execution time of script and has better performance.

Note: Abuse of keep-alive will only make your application more stuttered, because it will take up more memory for a long time.

Destruction of events

When a component is destroyed, we should clear the global events and timers added in the component to prevent memory leakage, etc.
Vue3's HOOK allows us to write the declaration and destruction of events together, which is more readable

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

onBeforeUnmount(()=>{
  ("scroll", scrollFun)
})

Vue2 can still achieve this effect through $once. Of course, you can also destroy events in optionsAPI beforeDestroy, but I recommend the writing method of the former, because the latter will make the code of the same function more scattered.

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

this.$once('hook:beforeDestroy', ()=>{
  ("scroll", scrollFun)
})

function scrollFun(){ /* ... */}

export default {
  created() {
    ("scroll", scrollFun)
  },
  beforeDestroy(){
    ("scroll", scrollFun)
  }
}

Image loading

Lazy image loading: suitable for situations where there are many pictures on the page and not all pictures are displayed in one screen. The vue-lazyload plug-in provides us with a very convenient lazy image loading instruction v-lazy

However, not all pictures are suitable for lazy loading, such as banners, photo albums, etc. It is more recommended to use image preloading technology, and the previous and next pictures of the currently displayed picture are downloaded first.

Adopt reasonable data processing algorithm

This relatively tests the foundation of data structures and algorithms
For example, a method to convert an array into a multilevel structure

/**
  * Array to tree structure, time complexity O(n)
  * @param list array
  * @param idKey element id key
  * @param parIdKey element parent id key
  * @param parId The parent id value of the first-level root node
  * @return {[]}
  */
function listToTree (list,idKey,parIdKey,parId) {
    let map = {};
    let result = [];
    let len = ;

    // Build map    for (let i = 0; i &lt; len; i++) {
        //Convert the data in the array into a key-value pair structure (the array and obj here will refer to each other, which is the focus of the algorithm implementation)        map[list[i][idKey]] = list[i];
    }

    // Build a tree array    for(let i=0; i &lt; len; i++) {
        let itemParId = list[i][parIdKey];
        // Top-level node        if(itemParId === parId) {
            (list[i]);
            continue;
        }
        // Orphan node, abandon (its parent node does not exist)        if(!map[itemParId]){
            continue;
        }
        // Insert the current node into the child of the parent node (since it is a reference data type, the node changes in obj, and the corresponding node in result will change accordingly)        if(map[itemParId].children) {
            map[itemParId].(list[i]);
        } else {
            map[itemParId].children = [list[i]];
        }
    }
    return result;
}

other

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

  • Freeze the object (avoid data that does not need responsiveness becomes responsive)
  • Long list rendering - batch rendering
  • Long list rendering - dynamic rendering (vue-virtual-scroller
  • ...

Home screen/volume optimization

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

  • volume
  • Code segmentation
  • network

Volume optimization

  • Compression and packaging code: The production environment packaging of webpack and vite will compress your code by default. This generally does not require special processing. Webpack can also be manually implemented through the corresponding compression plug-in.
  • Cancel source-map: You can check whether there is a .map file in your packaged product. If so, you can set the value of source-map to false or empty to turn off the code mapping (this takes up really large volume)
  • Enable gizp compression in package: This requires the server to enable gizp transmission, otherwise it will be useless if enabled (webpack has corresponding gzip compression plug-ins, and the less-version webpack compression plug-ins may be different. It is recommended to check it first)

Code segmentation

The function of code segmentation divides the packaging products into small products one by one, which depends on esModule. So when you use the import() function to import a file or dependency, the file or dependency will be packaged separately as a small product. Both the route lazy loading and asynchronous components use this principle.

  • Lazy route loading
  • Asynchronous Components

For UI libraries, I generally do not use on-demand loading components, but prefer the method introduced by CDN to optimize.

network

  • CDN: First of all, the CDN introduction mentioned above is introduced. The local library is used in the development stage, and these dependencies are excluded by configuring external extensions when packaging. Then introduce them in the html file through CDN
  • Server Push: HTTP2 is relatively mature; through the introduction of the above CDN, we can use the Server Push function of HTTP2 on the website to enable the browser to load these CDNs and other files in advance.
  • Turn on gzip: This has been mentioned above. The principle is that when both the client and the server support gzip transmission, the server will give priority to sending files compressed by gzip, and then the client receives them and is decompressing.
  • Turn on cache: Generally, I use negotiated cache, but this does not apply to all situations. For example, for files that use Server Push, they cannot modify their file names at will. So I usually fix the file name of the main files produced.

This is the end of this article about 22 Vue optimization techniques (project practical). For more related Vue optimization techniques, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!