Vue Performance: Computed vs Methods
Say you want to display a list of bicycles in a Vue component. To also show if a bicycle belongs to the current user, you need to call an expensiveFunction
for each bicycle. This function might be a call to an API or a complex calculation.
The Intuitive Way
What I encountered a lot of times is that unexperienced Vue developers would use a method isBicycleOwnedByUser
for this. Because the concept of methods is very close to plain javascript, they are the first thing you might think of.
And as long as you don't call this method more than once, there is actually no performance problem, yet.
Number of calls to expensiveFunction
: bicycles.length
<template>
<div>
<ul>
<li v-for="bicycle in bicycles">
<a
:href="'/bicycles/' + bicycle.id"
:class="{ 'red-500': isBicycleOwnedByUser(bicycle) }"
>
{{ bicycle.name }}
</a>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const isBicycleOwnedByUser = (bicycle: Bicycle) =>
expensiveFunction(bicycle.id);
</script>
The Problem
The problem arises if you need some additional text depending on the bicycle ownership. As soon as you call the method twice, i.e. isBicycleOwnedByUser
is called two times with the same parameter), the expensiveFunction
is also called twice.
Number of calls to expensiveFunction
: bicycles.length * 2
<template>
<div>
<ul>
<li v-for="bicycle in bicycles">
<a
:href="'/bicycles/' + bicycle.id"
:class="{ 'red-500': isBicycleOwnedByUser(bicycle) }"
>
{{ bicycle.name }}
</a>
<span>{{ isBicycleOwnedByUser(bicycle) ? 'Owned' : 'Not Owned' }}</span>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const isBicycleOwnedByUser = (bicycle: Bicycle) =>
expensiveFunction(bicycle.id);
</script>
The Performant Way
Instead of calling the method twice, we can use a computed property. It will evaluate once and we can use it as often as we want without any additional cost.
To avoid accidentally calling the methods more than once I would recommend to always use a computed property, even if you only need it once.
Number of calls to expensiveFunction
: bicycles.length
<template>
<div>
<ul>
<li v-for="bicycle in bicyclesWithOwnership">
<a
:href="'/bicycles/' + bicycle.id"
:class="{ 'red-500': bicycle.isOwnedByUser }"
>
{{ bicycle.name }}
</a>
<span>{{ bicycle.isOwnedByUser ? 'Owned' : 'Not Owned' }}</span>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const bicyclesWithOwnership = bicycles.map((bicycle) => ({
...bicycle,
isOwnedByUser: expensiveFunction(bicycle.id),
}));
</script>
TL;DR
- Always use computed properties when you need to use the same value multiple times.
- Use computed properties when you do not need to pass any arguments.
- Use methods only when you absolutely need to pass arguments.