【问题标题】:Computed object not updated using sync modifier未使用同步修改器更新计算对象
【发布时间】:2018-04-11 17:23:07
【问题描述】:

我有一个简单地格式化日期的计算属性:

computed: {
    items() { 
        return this.licenseItems.map(function(license) {
             license.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
             return license;
        });
    }
}

我将licenseItems 传递给带有.sync 修饰符的表单组件,并从表单中发出update:field 事件。在 vue 开发工具中,我可以看到 licenseItems(数据)已正确更新,但 items(已计算)仍显示旧数据,因此未执行重新计算。

我注意到,如果我删除地图并从计算属性中返回 licenseItems 对象,它会被更新。使用同步修饰符时,Vue 在映射对象上的计算属性是否存在问题?

【问题讨论】:

  • 这里没有足够的信息继续下去?你能展示它是如何在父模板中传递的以及它是如何在子模板中发出的吗?您是否正在向许可证中添加新属性?
  • 你是传递数组还是数组的一个元素给组件?
  • 好吧,两者都有,但我担心的是通过:items.sync="licenseItems"。同步工作正常,只是计算的属性没有更新。
  • 必须是我的组件特有的东西,我把 jsfiddle.net/kvej67xy/2 放在一起来测试这个功能,它似乎工作正常。

标签: vue.js vuejs2


【解决方案1】:

您应该知道您正在修改计算中的底层对象。从你的小提琴:

  computed: {
    mapped: function() {
      return this.items.map(function(item) {
        let original = item.date;
        item.date = moment(item.date).format('MM/DD/YYYY');
        console.log(original + ' => ' + item.date);
        return item;
      });
    }
  }

您的incrementDates 函数还直接修改底层对象。

因为items 的每个元素都是一个对象,item 是对同一对象的引用,因此您的例程会更新items 本身的成员。如果您打算修改对象值,则应使用watch 而不是computed。如果你想有一个不修改数据项的正确计算,你需要deep copy对象。

在下面的示例中,您可以看到数据值和计算值是不同的,但计算值是基于数据的。此外,update 事件与sync 一起使用以更新父级中的值,而不是直接在组件中更新值。您可以输入Date 理解的任何日期格式来设置值。

new Vue({
  el: '#app',
  data: {
    licenseItems: [{
      expires_at: Date.now()
    }]
  },
  computed: {
    items() {
      return this.licenseItems.map(function(license) {
        const newItem = Vue.util.extend({}, license);

        newItem.expires_at = moment(license.expires_at).format('MM/DD/YYYY');
        return newItem;
      });
    }
  },
  components: {
    myUpdater: {
      props: ['items'],
      methods: {
        doUpdate(event, index) {
          const newObj = this.items.map(item => Vue.util.extend({}, item));
          
          newObj[index].expires_at = new Date(event.target.value);
          console.log("new object", newObj);
          this.$emit('update:items', newObj);
        }
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <my-updater :items.sync="licenseItems" inline-template>
    <div>
      <input v-for="item, index in items" :value="item.expires_at" @change="doUpdate($event, index)">
    </div>
  </my-updater>
  <div v-for="item in items">
    {{item.expires_at}}
  </div>
</div>

【讨论】:

  • 是的,我实际上刚刚意识到我在小提琴中完全排除了 $emit,所以这证明了你的观点,即我正在修改引用的对象而不是对象的副本。感谢您将这些放在一起,我肯定忽略了 map 将在此处使用对原始对象的引用这一事实。
猜你喜欢
  • 2020-09-25
  • 1970-01-01
  • 1970-01-01
  • 2023-01-04
  • 1970-01-01
  • 2017-03-23
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多