为什么VueJS检测到此属性被添加到数组中的对象?

时间:2018-02-15 16:05:23

标签: javascript vue.js vuejs2

在下面的代码片段中,我有两个Vue组件,每个组件都有一个包含两个对象的items数组。如果该对象的items属性是真实的,则每个组件的模板都设置为显示show数组的对象。数组中的第一个对象最初的show值为true。第二个对象没有设置show值。



new Vue({
  el: '#ex1',
  data: () => ({ items: [{name: 'apple', show: true}, {name: 'banana'}] }),
  mounted() {
    // 2nd item's show property setting is not detected here
    this.items[1].show = true; 
  }
})

new Vue({
  el: '#ex2',
  data: () => ({ items: [{name: 'apple', show: true}, {name: 'banana'}] }),
  mounted() {
    // 2nd item's show property setting IS detected...
    this.items[1].show = true; 
    
    // ...when the 1st item's show property is set to false
    this.items[0].show = false; 
  }
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="ex1">
  Example 1:
  <div v-if="items[0].show">{{ items[0] }}</div>
  <div v-if="items[1].show">{{ items[1] }}</div>
</div>

<div id="ex2">
  Example 2:
  <div v-if="items[0].show">{{ items[0] }}</div>
  <div v-if="items[1].show">{{ items[1] }}</div>
</div>
&#13;
&#13;
&#13;

在第一个示例中,我将show数组中第二个对象的items属性设置为true。 Vue没有检测到模板中的变化,这是预期的。

在第二个示例中,我将第二个对象的show属性设置为true,然后将第一个对象的show属性设置为false。在这种情况下,Vue 检测items数组的两个元素的更改并相应地更新模板。鉴于Vue的Change Detection Caveats,我不希望出现这种情况。

这似乎是一个非常无害的错误(如果它甚至可以作为一个错误),所以我不确定它是否值得报告。但是,我想知道是否有人能够解释这种行为。为什么Vue在第二个示例中检测到对象添加了show属性?

2 个答案:

答案 0 :(得分:4)

更改被动属性(第一个对象的show)会触发重新渲染。 Vue忠实地呈现模板引用的对象的当前状态,其中包括第二个对象。

第二个对象的show属性仍然没有被动,对它的更改不会更新Vue。它只是对触发更新的第一个对象上的被动属性状态的更改。

换句话说,更改第二个对象的show属性,因为它是在事后添加的,将永远不会更新Vue,但更改第一个对象上的show属性将始终更新,并且更新将只渲染数组的当前状态,无论它是如何添加的。

我不会认为这是一个错误。

一种简单的方法是在更新后将数组记录到控制台。您将看到第二个对象的show属性未被观察到,但仍然存在。

修改 我想根据评论稍微澄清一下。第二个对象的更改出现在Vue中的原因是因为整个Vue被重新渲染而第二个对象直接在Vue中被引用。例如,如果您将值传递给组件,则不会重新呈现第二个对象。

这是一个示例,显示当值传递给组件时,第二个对象永远不会更新(因为组件管理渲染)。

&#13;
&#13;
console.clear()
 
 Vue.component("item", {
  props: ["item"],
  template: `
    <div>Item Show: {{item.show}}</div>
  `
 })
 
 new Vue({
      el: '#ex2',
      data: () => ({ items: [{name: 'apple', show: true}, {name: 'banana'}] }),
      mounted() {
        // 2nd item's show property setting IS detected...
        this.items[1].show = true; 
        
        // ...when the 1st item's show property is set to false
        this.items[0].show = false; 
      }
    })
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="ex2">
  This will show false, because that is the current state and this object's show
  property is reactive
  <item :item="items[0]"></item>
  <hr>
  This will not show *anything*, despite the current state of show=true
  because the object's show property is not reactive
  <item :item="items[1]"></item>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:-1)

如您发布的链接中所述:

Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(对象,键,值)

解决而不是 this.items [1] .show = true 你可以写:

Vue.set(this.items[1], 'show', true)

希望它清楚