最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - why vue change specific array member not update dom - Stack Overflow

programmeradmin7浏览0评论

I want to know why I changed the specific item of an array and the page doesn't update. I know doc of vue.js points out that:

Due to limitations in JavaScript, Vue cannot detect the following changes to an array:

When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue.

It says we should do that, but I don't know why. And I've found a similar question(Vue puted issue - when will it pute again) about that.

Here is some code from the question above:

// works well

data() {
  return {
    cart: {
      item: {
        nums: 10,
        price: 10,
      },
    },
  }
},
puted: {
  total() {
    return this.cart.item.nums * this.cart.item.price
  },
},
methods: {
  set() {
    //why it worked.
    this.cart.item = {
      nums: 5,
      price: 5,
    }
  },
},

// oops! not working!

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

I'm confused about the answer from the question:

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.

If I have replaced the object in this.cart[0], why this.cart[0] isn't marked as changed? why this.cart[0].price and nums still point to old object? I have changed the this.cart[0]! right?

And why in the first situation it works well? also replace the object . What's the difference between the two scenarios?

I want to know why I changed the specific item of an array and the page doesn't update. I know doc of vue.js points out that:

Due to limitations in JavaScript, Vue cannot detect the following changes to an array:

When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue.

It says we should do that, but I don't know why. And I've found a similar question(Vue puted issue - when will it pute again) about that.

Here is some code from the question above:

// works well

data() {
  return {
    cart: {
      item: {
        nums: 10,
        price: 10,
      },
    },
  }
},
puted: {
  total() {
    return this.cart.item.nums * this.cart.item.price
  },
},
methods: {
  set() {
    //why it worked.
    this.cart.item = {
      nums: 5,
      price: 5,
    }
  },
},

// oops! not working!

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

I'm confused about the answer from the question:

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.

If I have replaced the object in this.cart[0], why this.cart[0] isn't marked as changed? why this.cart[0].price and nums still point to old object? I have changed the this.cart[0]! right?

And why in the first situation it works well? also replace the object . What's the difference between the two scenarios?

Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Aug 22, 2019 at 18:24 ArchsxArchsx 9723 gold badges13 silver badges22 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 12

Vue is pretty explicit about this. It's a JavaScript limitation. JavaScript does not support the ability to detect when an array element changes, only when arrays change size as a result of adding or removing elements. Therefore replacing an element is not detectable.

What Vue does behind the scenes is sets up mechanics to watch for when objects change, which is something JavaScript does support. So array elements that are objects are things Vue can detect changes for. Since Vue cannot detect an array element being replaced, it doesn't know to stop looking at the old object and start looking at the new one.

The answer is also well-documented: if you want to update an item in an array, then use Vue.set(). This allows Vue to know that the array element is changing, so it knows to stop looking at the old object and start looking at the new one.

The solution therefore looks something like this:

Vue.set(this.cart, 0, {nums: 5, price: 5});

To make it short: Arrays are not registered reactive.

To assign a value use one of those equivalents:

  • Vue.set(array, index, value)
  • vm.$set(array, index, value)
  • array.splice(index, 1, value)

In the Observer class constructor(value) it is distinguished between Array and not Array:

if (Array.isArray(value)) {
  // steps through all indices
  this.observeArray(value)
} else {
  // steps through all keys
  this.walk(value)
}

But what is this.observeArray(value: Array) ?

For Arrays it actually just calls new Observer(value[index]) - the constructor(value[index])

There is no getter nor setter placed to watch for changes in this iteration over an Array


To inspect the second path: this.walk(obj: Object):

It calls defineReactive(obj, keys[i]) wich will in a simplified version will do:

defineReactive(obj, key=keys[i]){
  let val = obj[key]
  Object.defineProperty(obj, key, {

    get(){
      // registers in Deb
      dep.depend()
      // if array also 
      return val
    },
    set(newVal){

      val = newVal
      // adds to subscriber
      deb.notify()
    }
  })
}

But array proptotypes are patched to use the Observer this.__ob__.deb to notify on usage.


An alternative is the usage of set registered as:

  • Vue.prototype.$set
  • Vue.set

For Arrays Vue.set(array, index, value) calls array.splice(index, 1, value) and this again is the same as ob.observeArray(value) and ob.deb.notify()

Another solution for your set() method instead of use Vue.set() can be:

set() {
    this.cart.splice(0, 1, {
      nums: 5,
      price: 5,
    });
  },
发布评论

评论列表(0)

  1. 暂无评论