DEV Community

Marina Mosti
Marina Mosti

Posted on • Updated on

Hands-on Vue.js for Beginners (Part 6)

Let's talk about computed properties.

So far you've learned how Vue handles its own local state, the one we put inside data, and how a component can handle its prop properties - the ones that get handed down by the parent.

However, there is a type of properties in Vue which are called Computed Properties. Let's take a look at these today.

We're going to use a clean slate today, so that we can build a clear example. Here's the code.

<html>

<head>
  <title>Vue 101</title>
</head>

<body>
  <div id="app">
    <age-calculator></age-calculator>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    Vue.component('age-calculator', {
      template: `
        <p>
          NAME has been kicking butt for X days!
        </p>
      `
    });

    const app = new Vue({
      el: '#app'
    });
  </script>
</body>

</html>

I've gone ahead and added the scaffolding for an age-calculator component, right now it only outputs a <p> tag with an X where we're going to perform some sort of calculation. I've also added the corresponding tags <age-calculator> to the div#app.

If you need a refresher on basic components, take a look at Part V of this series, or better yet, nosedive the official docs!

When a simple prop isn't enough

Before we write any code, let's talk about what we're trying to accomplish.

I want to have a component where I pass it someone's age, and it will tell us how many days they've been around to enjoy avocados. If you don't like avocados then this is where our relationship ends, have a nice life.

Let's tackle the easy tasks that we already know how to accomplish, first we need an array of people with ages so that we can loop through it and output a bunch of components. Let's add the corresponding data.

const app = new Vue({
  el: '#app',
  data: {
    people: [
      { name: 'Mario', age: 38 },
      { name: 'Luigi', age: 38 },
      { name: 'Samus', age: 31 },
      { name: 'Link', age: 20 },
      { name: 'Marina', age: 32 },
      //Add yourself here :)
    ]
  }
});

Now, let's set up our v-loop to output an age-calculator per each one.

<div id="app">
  <age-calculator v-for="person in people" :key="person.name"></age-calculator>
</div>

Awesome, now let's allow the age-calculator component to receive a person, remember we do this with a prop. So first, let's add this new prop to the component.

Vue.component('age-calculator', {
  props: {
    person: {
      type: Object,
      required: true
    }
  },
  template: `
    <p>
      {{ person.name }} has been kicking butt for X days!
    </p>
  `
});

Bonus! Before you learned that to declare the props that a component can receive, you set up an array of strings props: ['person'] and this is fine in most cases. But what happens if we want a bit more control?

You can also, like in this case, set props to be equal to an object. Inside this object, we can create a property per each property we want to declare.

Inside the property declaration, in this case person, we can set some configuration values.

type to declare which (duh) type of data we're passing, so Object, Array, String, Number for example.

required is a boolean that allows us to mark this property as required for the component to work.

You can also set a default value, but we're not going to use that here.

Next, look at the template. We are now outputting the person's name {{ person.name }} onto the <p> tag.

One more thing before we can actually run this in our browser though. Can you tell what we're missing?

We still need to pass the actual person to the age-calculator component!

Go into the render loop and pass in our variable.

<age-calculator 
  v-for="person in people" 
  :person="person"
  :key="person.name"></age-calculator>

Go ahead and run this in your browser to check that everything is working. Baby-steps!

Note Before we move on, if you're curious what setting a prop to required will do for you, try removing this last bit we did when we pass the person to the component and look at your dev tools in the console section.

Handy, ain't it? 👌

The actual Computed Property

Alright, enough setting up and review.

We still have one more feature to tackle inside our component, we want to calculate the number of days each person has been alive.

Granted, it's not a very hard calculation, we just have to multiply 365 times the number of years (we're not going to go hardcore with JS Dates here). And in fact, we could go ugly and direct and put this straight into our template.

template: `
        <p>
          {{ person.name }} has been kicking butt for {{ person.age * 365 }} days!
        </p>
      `

This works, sort of. But what happens when you require more logic? A harder computation, some ifs/ands/ors/whens/beers? Then you're in a real problem because you can't really put that much logic inside the template, or it's going to get unmanageable real quick.

Here's where computed properties shine. Computed properties are in the end functions, that will execute a bit of code, and return a value. This value is now treated like a property, which means we can straight up use it in our template.

Let's take a look at how to set it up. First, let's add the computed wrapper to our component.

Vue.component('age-calculator', {
  props: {
    person: {
      type: Object,
      required: true
    }
  },
  template: `
    <p>
      {{ person.name }} has been kicking butt for {{ person.age * 365 }} days!
    </p>
  `,
  computed: {
    // Computed props go here
  }
});

So far so good, in fact this structure is the exact same one we have been using for methods, remember? (If you're thinking a method could also solve our problem, you're on the right track - we'll talk about this in a minute.)

Let's create a new computed property called daysAlive, it needs to be a function, and it needs to return something.

computed: {
  daysAlive() { //Remember, computed props are functions in the end
    return this.person.age * 365
  }
}

Take note that just like in methods we need to access the person prop though this, only inside the template we can use it directly! Other than that, nothing too fancy going on.

Now let's use this new daysAlive prop in our template.

template: `
  <p>
    {{ person.name }} has been kicking butt for {{ daysAlive }} days!
  </p>
`,

Note that we're outputting the value of the daysAlive --property--, (aha moment here). Vue treats computed props as, well, props - so we can use this here as you would a props prop, or a data prop.

In fact, Vue makes it so that if you would need to use this prop inside a method for example, you would have to access it through this.daysAlive. Neat right? It ACTUALLY becomes a prop. 🤯

YAY, run it in the browser and bask in your awesomeness.

Methods vs Computed Properties

You may have noticed a lot of similarities between methods and computed props, I mean, they're basically identical at code level. However there is a CORE difference that you need to understand in order to harness them fully.

Computed properties get cached.

What this means is, in the simplest possible way to explain it, that behind the scenes Vue will "read" your code and look for reactive dependencies - so data props and props props. It will watch these properties, and whenever they change, Vue will recalculate the value of your computed property. If they don't change, it'll just use a cached/stored value.

Methods, on the other hand, are ran EVERY time - there is no caching, no code reading, no magic. They're just plain old functions.

Why does this matter? When are these functions called?

Every time your component/app re-renders (so every time a component's data change, or every time it's parent's data changes), Vue will figure out if that data is tied to a computed property, if it's not - it won't call this function again. For regular methods however, they will be re-run every time!

For this example, where we're doing a very simple calculation for these few objects it doesn't really matter, frankly. But when you start doing some serious code weightlifting on top of a thousand components, then you're going to want to leverage this caching or your app is going to take a hit on each render cycle.

If you want to read more, here's a link to the official docs regarding computed properties.

Here's the complete code for today.

<html>

<head>
  <title>Vue 101</title>
</head>

<body>
  <div id="app">
    <age-calculator 
      v-for="person in people" 
      :person="person"
      :key="person.name"></age-calculator>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    Vue.component('age-calculator', {
      props: {
        person: {
          type: Object,
          required: true
        }
      },
      template: `
        <p>
          {{ person.name }} has been kicking butt for {{ daysAlive }} days!
        </p>
      `,
      computed: {
        daysAlive() {
          return this.person.age * 365
        }
      }
    });

    const app = new Vue({
      el: '#app',
      data: {
        people: [
          { name: 'Mario', age: 38 },
          { name: 'Luigi', age: 38 },
          { name: 'Samus', age: 31 },
          { name: 'Link', age: 20 }
        ]
      }
    });
  </script>
</body>

</html>

That's it for today! Thanks for reading, and we'll keep on going next week with watchers! 🕵️‍♀️ 👀

Discussion (10)

Collapse
neutrino2211 profile image
Mainasara 🇳🇬

Never knew computed properties where cached, I have always used methods instead because I thought they were the same thing. Not anymore

Collapse
marinamosti profile image
Marina Mosti Author

Your training, complete is. :D

Collapse
pravinkumar95 profile image
Pravin kumar • Edited on

Since computed properties are read by vue before executing them, wouldn't it make complex functions slower than using it in methods? Methods are only executed right?
Assuming my elements change everyframe..

Collapse
ericstolzervim profile image
Eric Stolzervim

Useful and enjoyable, thank you.

Collapse
marinamosti profile image
Marina Mosti Author

Thanks for reading :)

Collapse
dsiss profile image
DS-ISS

Hi Marina,
Why do we not need a data() in the component like last lesson here?

Collapse
dsiss profile image
DS-ISS

I think I got it. Because we are not changing anything in "person" here.

Collapse
neehad profile image
Nihad Obralic

Very useful, thank you!

P.S. Great style of writing and explanations.

Collapse
namstel profile image
Namstel

Clear and concise with a dash of humor, love it!

Collapse
marinamosti profile image
Marina Mosti Author

Glad you enjoyed it :)