Vue 3 — Computed Properties and Watchers

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at Vue computed properties and watchers.

Computed Properties

We can add computed properties to derive some from an existing property.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="message" />
      <p>{{reversedMessage}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            message: ""
          };
        },
        computed: {
          reversedMessage() {
            return this.message
              .split("")
              .reverse()
              .join("");
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We added the message property to the object we return in data .

Then we added the computed property to the object we passed into createApp .

Methods are inside it getters, so we return things derived from other properties.

The method must be synchronous.

Now when we type in something to the input, we’ll see the reversed version displayed below it.

Computed Caching vs Methods

We can achieve the same result by running a method to reverse the string.

But it wouldn’t be cached based on their dependency if we use a regular method to return derived data.

Computed properties are cached based on the original reactive dependencies.

As long as this.message stays the same, reversedMessage won’t be run.

However, if we have dependencies that aren’t reactive in our computed property, then the method won’t run.

In this case, we’ve to use methods to make them update.

So something like:

computed: {
  now() {
    return Date.now()
  }
}
Enter fullscreen mode Exit fullscreen mode

won’t update.

Computed Setter

We can have setters in our computed properties.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="firstName" placeholder="first name" />
      <input v-model="lastName" placeholder="last name" />
      <p>{{fullName}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            firstName: "",
            lastName: ""
          };
        },
        computed: {
          fullName: {
            get() {
              return `${this.firstName} ${this.lastName}`;
            },
            set(newValue) {
              const names = newValue.split(" ");
              this.firstName = names[0];
              this.lastName = names[names.length - 1];
            }
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have the firstName and lastName properties in the object we return with data .

Then we can create the getter for the fullName property by returning the 2 properties combined into one string.

The setter would be the set method which takes the newValue and then split it back into its parts.

We set the this.firstName and this.lastName based on the combined string we have returned from get .

When this.firstName and this.lastName change then get is run.

If get is run, then set is run.

Watchers

Computed properties work for most cases, but sometimes we need watchers to provide us with a more generic way to watch for data changes.

For example, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="name" />
      <p>{{data}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            name: "",
            data: {}
          };
        },
        watch: {
          name(newName, oldName) {
            if (oldName !== newName) {
              this.getData(newName);
            }
          }
        },
        methods: {
          getData(newName) {
            fetch(`https://api.agify.io/?name=${newName}`)
              .then(res => res.json())
              .then(res => {
                this.data = res;
              });
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have a getData method which takes a newName parameter and get some data from an API.

And then we have our watch property, which has an object with watchers.

The name of the method would match the name of the instance property name.

A watcher takes an old value and a new value as the parameter.

And we can run what we want inside it.

The code we run is async so we got to use a watcher instead of computed properties.

Conclusion

Computed properties and watchers let us watch for reactive data changes and let us run code when they change.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .