如何更新被动对象(以及一般属性)?

时间:2017-06-03 16:10:54

标签: javascript vue.js

Reactivity in Depth上的Vue.js文档提及

  

数据对象中必须存在属性以便Vue   转换它并使其反应

(...)

  

您必须通过声明所有根级别来初始化Vue实例   前期的反应数据属性,即使只是一个空值

考虑这两个代码片段,其中tags被定义为空对象并在脚本过程中以两种不同的方式更新:



var vm = new Vue({
  el: "#root",
  data: {
    tags: {}
  }
});

vm.tags = {
  hello: true,
  world: true
};

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="root">
  {{tags}}
</div>
&#13;
&#13;
&#13;

&#13;
&#13;
var vm = new Vue({
  el: "#root",
  data: {
    tags: {}
  }
});

vm.tags["hello"] = true
vm.tags["world"] = true
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="root">
  {{tags}}
</div>
&#13;
&#13;
&#13;

在第一种情况下,内容被正确更新,而在第二种情况下 - 不是。

为什么会这样,尽管在两种情况下都在VM实例化中声明了tags

2 个答案:

答案 0 :(得分:4)

并不是声明tags对象,而是在实例化Vue时tags对象上的属性不存在。

在第二种情况下,您使用索引器向tags对象添加两个新属性。 Vue 无法检测到添加了这些属性。

这就是$set方法存在的原因。向对象添加新属性时,需要通过$setVue.set

添加
 Vue.set(vm.tags, 'hello', true)

或者,如果你在Vue方法中,

this.$set(this.tags, 'hello', true)

第一个的情况下,您要添加一个完全不同的对象具有属性。在这种情况下,Vue知道这些属性,并在将新值添加到数据时将它们转换为被动属性。

如果而不是你添加了一个新的空对象而然后添加了属性,那么你将回到与第二个例子相同的情况。

通常,您只想使用空属性初始化对象。

data: {
    tags:{
        hello: false,
        world: false
    }
}

在这种情况下,属性将转换为反应属性,并且将检测到更改。

修改

@WoJ用笔发布评论,代码如下:

&#13;
&#13;
var vm = new Vue({
  el: "#root",
  data: {
    posts: {},
    tags: {}
  }
});

vm.tags = {
  hello: true,
  world: true
};

vm.posts["bonjour"] = true
vm.posts["monde"] = true
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="root">
  <!-- show all tags which are on -->
  {{tags}}
  {{posts}}
</div>
&#13;
&#13;
&#13;

在此代码中,出现,Vue的posts属性将使用新的反应属性进行更新,因为它们在呈现Vue时显示在输出中。这里发生的是 属性被添加到posts对象,这只是javascript的工作原理,你可以添加属性到对象,但 Vue 不知道他们在那里。更具体地说,这些属性不会作为被动属性(getters / setter)添加,这是Vue在添加到Vue的数据发生更改时所知道的。实际上,将这些属性添加到posts对象不会触发渲染

那么,为什么新属性会出现在输出中呢?新posts属性显示在输出中的原因是因为将tags属性设置为新对象会触发要调度的渲染。 了解Vue呈现同步,它们是异步的非常重要(请参阅here)。

  

... Vue执行DOM更新   异步。每当观察到数据变化时,它将打开一个   排队和缓冲在同一事件中发生的所有数据更改   循环。

     

例如,当您设置vm.someData =&#39;新值&#39;时,该组件   不会立即重新渲染。它将在下一个“tick”中更新,   刷新队列时。

在上面的代码示例中,对tags的更新会触发要调度的渲染。然后,使用转换为反应属性的两个新属性更新posts对象,因为Vue不知道它们存在。 然后一段时间后,计划的渲染发生,Vue使用数据中对象的当前状态更新HTML。由于posts 具有这两个新属性,因此这些属性会呈现给屏幕。但是,对这些属性的更新永远不会触发更新以进行渲染。

要查看这种情况,只需注释掉tags属性的更新即可。

&#13;
&#13;
var vm = new Vue({
  el: "#root",
  data: {
    posts: {},
    tags: {}
  }
});

//vm.tags = {
//  hello: true,
//  world: true
//};

vm.posts["bonjour"] = true
vm.posts["monde"] = true
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="root">
  <!-- show all tags which are on -->
  {{tags}}
  {{posts}}
</div>
&#13;
&#13;
&#13;

注意,在这种情况下,渲染的Vue 从不更改。

答案 1 :(得分:2)

这是因为在第一种情况下,您在tags上运行 setter (因为您正在重新分配它) - Vue已经包装并可以检测到。在第二种情况下,您在data: { tags: {定义中的的嵌套属性上运行setter,因此它们不会被动反应。

文档中的Change Detection Caveats部分涵盖了这一点,但完全与您的情况相同(嵌套属性情况)。您必须声明您的数据:

data: {
  tags: {
    hello: null,
    world: null,
  }
}