3

Vue computed has already perplexed me for a while

when will it compute again

condition1:

data() {
  return {
    cart:{
      item:{
        nums: 10,
        price: 10
      }
    }
  };
},
computed: {
  total() {
    return this.cart.item.nums * this.cart.item.price
  }
},
methods:{
  set(){
    this.cart.item = {
       nums: 5,
       price: 5
    }
  }
}

computed will work


condition2:

data() {
  return {
    cart: [{
        nums: 10,
        price: 10
    }]
  };
},
computed: {
  total() {
    return this.cart[0].nums * this.cart[0].price
  }
},
methods:{
  set(){
    this.cart[0] = {
       nums: 5,
       price: 5
    }
  }
}

computed won't work


I know this is the solution, but why?

methods:{
  set(){
    this.cart[0].nums = 5
    this.cart[0].price = 5
    }
  }
}

why didn't it be observed in condition2 ?

why Vue don't want it be observed ?

1

1 Answer 1

7

Reactivity with objects and arrays is a bit finicky with Vue. With other variables it is easy to detect when they are changed, but with objects and arrays it is not always possible to detect whenever something in the object/array has been changed. (That is, without Proxies, which will come in Vue 3.x)

In your case, total will be recalculated if this.cart is marked as changed, this.cart[0] is marked as changed or if this.cart[0].nums or this.cart[0].price is changed. The problem is that you are replacing the object in this.cart[0]. This means that this.cart[0].price and nums do not change, because those still point to the old object. Apparently, this.cart[0] and this.cart are not marked as changed, so Vue still believes total to be up-to-date.

There are several ways to get around this. One is to use Vue's helper methods to work with objects/arrays, namely Vue.set, Vue.delete. You can access them in your SFC with this.$set or this.$delete. As this.$set explicitly marks whatever you pass as first argument as "changed", your total will also be updated.

this.$set(this.cart, 0, {
  nums: 2,
  price: 100
});

Another way is to modify the object itself, rather than replacing it. Since you are still working with the same object, Vue will detect that this.cart[0] has changed.

setItem() {
  this.cart[0] = Object.assign(
    this.cart[0],
    {
      nums: 5,
      price: 5
    }
  );
}

Another way is to use one of the many array methods. In your case, you could use Array.prototype.splice. As this is a function call, Vue can detect that the function is called and can mark the correct items as changed, which will trigger an update of anything that relies on it.

this.cart.splice(0, 1, {
  nums: 50,
  price: 10
});

Edit Vue + Vuex + VueRouter template

Sign up to request clarification or add additional context in comments.

2 Comments

But I also replaced the object( this.cart.item ) in condition1. why did Vue know this.cart.item.nums and price was changed in this case?
It works there, because you are changing a key that already exists on the object. The only problem with objects I know of is when you try to set a key that did not previously exist. For example, if you tried to set the title key of the item in the cart, it would not update anything, because title was not in the object when you set it first. Normally you would need to call this.$set to get that to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.