Skip to content
Advertisement

What’s causing the Vue computed property to be computed?

My understanding of computed properties is that if the computed property is not used in the template it’s not supposed to be computed. However, when there is a watcher for a computed property, this computed property is evaluated.

I understand it’s not desired application architecture, but my question is if the fact that it’s working this way is a desired Vue behaviour or some side-effect of how things are implemented right now and can change in future releases?

Or to put the question in other words: Should the computed property be evaluated if it’s not used in the template and it has no other explicit getter in the codebase, except a watcher?

Example:

<template>
  <div>
        <button @click="counter++">+1</button>
        {{ counter }}
  </div>
</template>

<script>
export default {
  name: "Main",
  data() {
    return {
      counter: 0,
    };
  },
  computed: {
    notUsedInTemplate() {
      console.log("notUsedInTemplate computed called");
      return this.counter + 1;
    },
  },
  watch: {
    notUsedInTemplate() {
      console.log("notUsedInTemplate watch called");
    },
  },
};
</script>

Link to sandbox: https://codesandbox.io/s/computed-sandbox-ru3n3?file=/src/components/Main.vue:0-818

Advertisement

Answer

if the computed property is not used in the template it’s not supposed to be computed

…is not correct. Correct statement is:

if the computed property is not used (anywhere) it’s not supposed to be evaluated

Using watch evaluates the property. In fact watch is very similar to template re-rendering (In vue 3 re-rendering is in fact little special watchEffect)

Because to watch (anything):

  1. Vue 1st needs to run the code – in this case notUsedInTemplate getter – to find out what reactive data it is accessing (so this.counter is tracked as dependency)
  2. When this.counter changes, watcher runs the notUsedInTemplate getter again so it can compare new value to previous one
  3. If value changed, it runs the callback (which is logging in your example)

So to answer the questions:

  1. yes this is desired behavior as your watcher is not tracking changes of notUsedInTemplate computed property but underlying data it uses to compute it’s value
  2. Watcher is accessing the computed property, there is no need to use it anywhere else

OK, more details WHY this works this way

Vue reactivity has always 2 phases:

  1. phase 1 – run the code (be it template or watch expression/fn) and collect all dependencies (e.g. any reactive data the code “touched”)
  2. phase 2 – track the dependencies. If any dependency changes, re-run callback (or render function)

See this chapter in the docs – How Changes Are Tracked – the example is explicitly mentioning only template re-rendering (which uses watcher created by Vue itself) but all of this applies to all watchers including those created by the user

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement