Computed properties are easily one of my favorite expressions of Vue's reactive paradigm. They allow us to store a reference to a result, abstracting the tracking of changes and cleaning out logic from our templates1.
Sometimes computed properties are complex units of logic, running loops, building internal data structures and finally churning out a result. More often, though, I find myself using them for simpler things — concatenating strings, multiplying one thing by another, mapping an array of objects to expose specific properties. Exactly the kinds of jobs that arrow functions were made for.
How
Functional computed properties are written in the following way:
computed: {
fullName: ({ firstName, lastName }) => firstName + ' ' + lastName
}
Let's break that down. The regular syntax for that computed property would be:
computed: {
fullName () {
return this.firstName + ' ' + this.lastName
}
}
Not too bad, but a bit verbose. If we were writing regular old ES6, outside of a Vue component, we'd probably want to turn that into an arrow function. However, inside our component the following code wouldn't work:
// DOES NOT WORK
computed: {
fullName: () => this.firstName + ' ' + this.lastName
}
That's because a central feature of arrow functions is that they bind their parent's lexical scope, which is a fancy way of saying that they don't have their own this
. We would end up with a TypeError
, telling us that this
is undefined
.
So we know we need to get our component's this
into the function, but from where? Well, it turns out that Vue injects the whole component as the only argument of every computed property! That how we have access to this
in the regular syntax. You can test this for yourself like this:
computed: {
testThis () {
console.log(arguments)
}
}
Javascript's magical arguments
variable represents an array of all the arguments provided to a function. Once you load the component, you'll see in the console an array containing a single object — your Vue component, complete with your data, computed, methods, $root
etc.
So now we can write our property as:
computed: {
fullName: (this) => this.firstName + ' ' + this.lastName
}
The final touch is destructuring this
, pulling in only the properties we need:
computed: {
fullName: ({ firstName, lastName }) => firstName + ' ' + lastName
}
Why
So what have we gained? Well, for starters we've turned our 3 lines into a punchy one-liner. We've made it clear that this is a function that takes in some values and returns a new value, with no side effects. Finally, we've made it explicit what data the computed property depends on and tracks — no matter what other data is in the component, we know that this property can only depend on the values we've provided it.
If we have many computed properties, they're easier to skim and reason about:
computed: {
fullName: ({ firstName, lastName }) =>
firstName + ' ' + lastName,
shortNameToDisplay: ({ firstName }) =>
firstName || 'Please enter your name',
isInFirstNamesList: ({ firstNamesList, firstName }) =>
firstNamesList.indexOf(firstName) !== -1,
// we can even call the fullName computed property,
// since it's available in the component as this.fullName
nameUrl: ({ baseUrl, fullName }) =>
baseUrl + '/' + fullName.toLowerCase(),
}
A great use case is for 'destructuring' props to simplify templates in presentational components:
<template>
<img :src="photoUrl">
<h3>{{ firstName }}</h3>
<p>{{ bio }}</p>
</template>
<script>
export default {
name: 'PersonCard',
props: {
person: { type: Object }
},
data () {
baseUrl: 'https://foo.bar/baz'
},
computed: {
firstName: ({ person }) => person.firstName,
bio: ({ person }) => person.bio,
photoUrl: ({ baseUrl, person }) => baseUrl + '/' + person.photo
}
}
</script>
When
As a rule of thumb, functional computed properties (and arrow functions in general) are most useful when their logic is simple and requires no temporary variables. If you think of your property as "this thing in relation to that thing", it's probably a good candidate.
On the other hand, as soon as you find yourself adding braces to a functional property and running logic before you return
, it's probably time to refactor back to the regular syntax. Remember, the improvement here is solely to code readability, so the question should always be "is this easier to understand?"
-
As per the recommendation of the Vue style guide ↩
Top comments (1)
I've never used the destructuring approach with Computed properties before. Definitely going to start doing this!