【问题标题】:How to update reactive Objects (and properties in general)?如何更新反应性对象(和一般属性)?
【发布时间】:2017-11-04 20:53:44
【问题描述】:

Reactivity in Depth 上的 Vue.js 文档提到了这一点

一个属性必须存在于数据对象中才能让 Vue 转换它并使其具有反应性

(...)

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

考虑这两个代码 sn-ps,其中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>

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

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

在第一种情况下,内容已正确更新,而在第二种情况下 - 没有。

为什么会这样,尽管在这两种情况下tags 都是在 VM 实例化时声明的?

【问题讨论】:

    标签: javascript vue.js


    【解决方案1】:

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

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

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

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

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

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

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

    如果改为您添加了一个新的空对象并然后添加了属性,您将回到与第二个示例相同的情况。

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

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

    在这种情况下,属性将被转换为反应性属性并检测到变化。

    编辑

    @WoJ 用钢笔发表了一条评论,代码如下:

    var vm = new Vue({
      el: "#root",
      data: {
        posts: {},
        tags: {}
      }
    });
    
    vm.tags = {
      hello: true,
      world: true
    };
    
    vm.posts["bonjour"] = true
    vm.posts["monde"] = true
    <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>

    在这段代码中,显示 Vue 的 posts 属性使用新的响应属性进行了更新,因为它们在 Vue 渲染时显示在输出中。这里发生的是属性添加到posts对象,这就是javascript的工作原理,你可以给对象添加属性,但是Vue没有知道他们在那里。更具体地说,这些属性不会作为 reactive 属性(getter/setter)添加,这是 Vue 知道何时更改已添加到 Vue 的数据的方式。事实上,将这些属性添加到posts 对象不会触发渲染

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

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

    例如,当您设置 vm.someData = 'new value' 时,组件 不会立即重新渲染。它将在下一个“滴答”中更新, 当队列被刷新时。

    在上面的代码示例中,对tags 的更新会触发要安排的渲染。然后,posts 对象被更新为两个新属性,这些属性没有转换为响应式属性,因为 Vue 不知道它们存在。 然后 一段时间后,预定的渲染发生,Vue 用数据中对象的 当前 状态更新 HTML。由于posts 确实 具有这两个新属性,因此这些属性将呈现到屏幕上。然而,对这些属性的更新将永远触发渲染更新。

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

    var vm = new Vue({
      el: "#root",
      data: {
        posts: {},
        tags: {}
      }
    });
    
    //vm.tags = {
    //  hello: true,
    //  world: true
    //};
    
    vm.posts["bonjour"] = true
    vm.posts["monde"] = true
    <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>

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

    【讨论】:

    • 谢谢,我花了很多时间来调试它——也是因为 CodePen 的行为不同。 codepen.io/WoJWoJ/pen/bRGamg 例如表明我的两个例子都在工作......
    • @WoJ 它在你的笔中并没有真正的不同,它只是看起来像它一样。如果您在笔中进行更改后记录 vm.posts 和 vm.tags,您将看到 vm.posts not 响应式(未观察到它们)。如果您注释掉对 vm.tags 的更新,您将看到屏幕上从未呈现任何更改。这是因为对 vm.tags 的更改会导致安排渲染,并且当渲染发生时,两个属性都会显示。
    • 非常感谢 CodePen 案例的详细描述,特别指出触发后渲染的异步特性。这一点现在很清楚了。很遗憾我不能双重投票:)
    • @WoJ 很高兴为您提供帮助 :)
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2016-03-31
      • 2022-12-11
      • 2016-08-08
      • 1970-01-01
      • 1970-01-01
      • 2013-01-18
      • 2022-11-26
      • 2021-06-04
      • 1970-01-01
      相关资源
      最近更新 更多