Vue 3 has provided us with several significant performance improvements out of the box but has also introduced some extra manual features that can help up improve our app performance.
In this article, we are going to discuss a new directive called v-memo
. This directive has been introduced in Vue 3.2 and to the best of my knowledge, it is not going to be available in Vue 2. The aim of this directive is to help you improve the performance of your medium / large Vue application and it is not intended to be used in small applications.
What does v-memo
do?
The definition of v-memo
is:
v-memo
is used to memoize a sub-tree of a component in relation to a fixed set of dependencies values. The sub-tree will delay all future updates until one of the dependencies values changes.What is v-memo
Even if the above sentence may seem scary, in reality, things are quite simple to understand. What v-memo
does is the same that already happens with our computed properties, but just applied to part of the DOM.
This new directive will cache the part of the DOM it controls, and just run an update and re-render it if a specific value changes. These values are manually set by the developers.
A basic example of v-memo
Examples are always the best way to understand new technologies, so let’s jump right in and provide a small example to start and see the syntax and usage of this directive:
<template>
<div>
..the rest of the component
<div v-memo="[myValue]">
<svg >
<title>{{MyValue}}</title>
...
</svg>
<vue-custom-element :value="myValue"></vue-custom-element>
</div>
</div>
</template>
Let’s see in detail what the above achieves:
...the rest of the component
The first line that needs explanation is the fact that v-memo is usually meant to be used as part of a component and it should just affect a subset of the component dom. So in this example, we expect the component to actually be bigger than what I just pasted above.
<div v-memo="[myValue]">
Next, we have assigned our v-memo
to a specific DIV and all its children elements. When calling v-memo we have to pass an array of values that are going to control the rendering of the sub-tree.
The array accepts one of more values v-memo="[valueOne, valueTwo]
” and also expressions like v-memo="myValue === true"
.
Note: Calling v-memo
with an empty array would be the equivalent of using v-once
and will render that part of the component just once.
<svg >
<title>{{MyValue}}</title>
...
</svg>
<vue-custom-element :value="myValue"></vue-custom-element>
It is now time to focus on the content of our sub-tree. In my example, I have used a native element “SVG” and a custom Vue element “vue-custom-element”. I have done this to highlight the fact that the HTML included in v-memo
can be absolutely anything.
When our inner HTML is a native element (div, SVG, button, form), using v-memo
would just be useful when used on very large datasets (like large tables) and or very complex nested DOM, like in the case of a dynamic SVG.
While, using v-memo
on a custom component is not only going to stop the rendering of HTML, but it is actually going to actually stop the reactive update of Vue components too (watch, computed, etc..). When used with Vue custom element, v-memo can be very useful for situations in which the rendering of the child component is very costly and we are able to “defer” its update to specific scenarios.
Few examples
This section is going to include a couple of quick examples to provide more context and help understand when this feature would really be useful.
I would like to emphasise that this feature is for advanced users and should just be used for complex applications where small performance improvements are really important.
The wrong case
I am going to start by writing when the use of v-memo
is actually incorrect and will not provide any performance advantages.
<div v-memo="[myValue]">
<p>Static content, no vue values here</p>
</div>
In the above example, the sub-tree included within v-memo
does not need to be memorized as it is actually static and will not change (it does not include any Vue variable). The Vue 3 virtual DOM management is able to automatically understand which part of the DOM is static and which one is not.
Adding the v-memo
on a static HTML is not going to be useful, no matter how complex the HTML is.
Managing updates
There are cases in which v-memo
could be used not only to improve performance but to actually improve the UX (user experience), by controlling the update cycle of the component.
<div v-memo="[allFieldChanged]">
<p>{{ field1 }}</p>
<p>{{ field2 }}</p>
<p>{{ field3 }}</p>
<p>{{ field4 }}</p>
</div>
In the above example changing an individual field, for example, field1 is not going to result in a re-render. In fact, the new fields will just be shown when all fields have been updated.
I had a situation recently where a child component would update and react to a large JSON dataset. In that case, using v-memo
really helped to just trigger the update when all the changes were completed.
Use it in conjunction with v-for
One of the most common use case for using v-memo
is when dealing with very large lists rendered using v-for
.
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
In the above example that has been taken directly from the Vue documentation, we see the v-for
/ v-memo
syntax in practice.
Without the use of v-memo
in the code above, each change of the “selected” variable would have resulted in a full re-render of the list. The memorization provided by the new directive, allows the framework to just update the rows in which the expression item.id === selected
has changes, therefore when an item is selected or unselected.
If we consider a list with 1000 entries. Using v-memo
with the above code, would save use the evaluation and re-rendering of 998 entries for each change! Not bad!
Inadvertently stop the child component to trigger updates
The last example is an edge case that I have encountered recently. In most of this article we have emphasises the fact that v-memo
will stop the sub-tree from rendering an update, but what is important to realize, is that using this directive will actually stop the execution of any code that may be triggered with an update.
Let’s take the following example:
<div v-memo="[points > 1000]">
<myComponent :points="points" />
</div>
//myComponent
<isLevel1 v-if="points <= 1000">....</isLevel1>
<isLevel2 v-if="points > 1000">...</isLevel2>
<script>
...,
watch: {
points() {
logPointChange();
}
}
If we assume the inner component “isLevel1” and “isLevel2” to be complex, avoiding their rendering using v-memo
seems like a good idea. Unfortunately avoiding re-rendering will also avoid the running of any code within the myComponent
. What this means in our example is that the watch registered on the property “points” is not going to run unless the variable “points” changes between the level threshold.
This problem is quite easy to solve, as all we need to do is move the log method to our parent, but it is important to remember that execution is completely truncated by the use of v-memo
.
Summary
This new directive is very powerful and with great power comes great responsibility. The use of it should really be the last option in the case in which performance is key and we are working on a large application.
As I will encounter more examples I will come back and expand this post to hopefully help people to really understand how to use this feature (and when not to), but I also urge you guys to suggest other examples or scenarios in the comments below to help future readers.
Top comments (0)