Vue.js计算属性在通过事件时失去其反应性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue.js计算属性在通过事件时失去其反应性相关的知识,希望对你有一定的参考价值。

我的主应用程序中有一个Modal组件,只要需要显示模式,就会通过事件传递内容。模态内容始终是一个列表,其中包含与每个项目关联的操作,例如“select”或“remove”:

Vue.component('modal', {
  data() {
    return {
      shown: false,
      items: [],
      callback: ()=>{}
    }
  },
  mounted() {
    EventBus.$on('showModal', this.show);
  },
  template: `<ul v-if="shown">
    <li v-for="item in items">
      {{ item }} <button @click="callback(item)">Remove</button>
    </li>
  </ul>`,
  methods: {
    show(items, callback) {
      this.shown = true;
      this.items = items;
      this.callback = callback;
    }
  }
});

遗憾的是,当将计算属性传递给该模态时,如下面的组件中,反应链接被破坏 - >如果操作是“删除”,则列表不会更新。

Vue.component('comp', {
  data() {
    return {obj: {a: 'foo', b: 'bar'}}
  },
  computed: {
    objKeys() {
      return Object.keys(this.obj);
    }
  },
  template: `<div>
    <button @click="showModal">Show Modal</button>
    <modal></modal>
  </div>`,
  methods: {
    remove(name) {
      this.$delete(this.obj, name);
    },
    showModal() {
      EventBus.$emit('showModal', this.objKeys, this.remove);
    }
  }
});

看看这个小提琴中的最小用例:https://jsfiddle.net/christophfriedrich/cm778wgj/14/

我认为这是一个错误 - 不应该Vue记得objKeys用于渲染Modal并更新它? (将obj的变化转发到objKeys工作。)如果没有,我出错了什么,我怎样才能达到我想要的结果?

答案

你有模态使用自己的items副本:

 template: `<ul v-if="shown">
    <li v-for="item in items">
      {{ item }} <button @click="callback(item)">Remove</button>
    </li>
  </ul>`,
  methods: {
    show(items, callback) {
      this.shown = true;
      this.items = items;
      this.callback = callback;
    }
  }

在调用show时,该副本会生成一次,而您复制的内容只是在您发出showModal事件时计算的值。 show收到的不是计算的,它分配的不是计算的。这只是一个价值。

如果在代码中的任何地方,你做了类似的任务

someDataItem = someComputed;

数据项不是计算机的功能副本,它将是分配时其值的快照。这就是为什么在Vue中复制值是一种不好的做法:它们不会自动保持同步。

您可以传递一个返回感兴趣的值的函数,而不是复制值。有效地获取功能。为了语法清晰,您可以根据该函数进行计算。然后你的代码变成了

const EventBus = new Vue();

Vue.component('comp', {
  data() {
    return {
      obj: {
        a: 'foo',
        b: 'bar'
      }
    }
  },
  computed: {
    objKeys() {
      return Object.keys(this.obj);
    }
  },
  template: `<div>
    <div>Entire object: {{ obj }}</div>
    <div>Just the keys: {{ objKeys }}</div>
    <button @click="remove('a')">Remove a</button>
    <button @click="remove('b')">Remove b</button>
    <button @click="showModal">Show Modal</button>
    <modal></modal>
  </div>`,
  methods: {
    remove(name) {
      this.$delete(this.obj, name);
    },
    showModal() {
      EventBus.$emit('showModal', () => this.objKeys, this.remove);
    }
  }
});

Vue.component('modal', {
  data() {
    return {
      shown: false,
      getItems: null,
      callback: () => {}
    }
  },
  mounted() {
    EventBus.$on('showModal', this.show);
  },
  template: `<div v-if="shown">
  <ul v-if="items.length>0">
    <li v-for="item in items">
      {{ item }} <button @click="callback(item)">Remove</button>
    </li>
  </ul>
  <em v-else>empty</em>
</div>`,
  computed: {
    items() {
      return this.getItems && this.getItems();
    }
  },
  methods: {
    show(getItems, callback) {
      this.shown = true;
      this.getItems = getItems;
      this.callback = callback;
    }
  }
});

var app = new Vue({
  el: '#app'
})
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <comp></comp>
</div>
另一答案

您正在将值传递给函数,而不是将prop传递给组件。道具是反应性的,但价值只是价值。你在modal的模板中包含comp,所以重新加工它(至少)items作为道具。然后它会被动反应。

我建议让删除过程遵循emit-event-and-process-in-parent而不是传递回调。

const EventBus = new Vue();

Vue.component('comp', {
  data() {
    return {
      obj: {
        a: 'foo',
        b: 'bar'
      }
    }
  },
  computed: {
    objKeys() {
      return Object.keys(this.obj);
    }
  },
  template: `<div>
    <div>Entire object: {{ obj }}</div>
    <div>Just the keys: {{ objKeys }}</div>
    <button @click="remove('a')">Remove a</button>
    <button @click="remove('b')">Remove b</button>
    <button @click="showModal">Show Modal</button>
    <modal :items="objKeys" event-name="remove" @remove="remove"></modal>
  </div>`,
  methods: {
    remove(name) {
      this.$delete(this.obj, name);
    },
    showModal() {
      EventBus.$emit('showModal');
    }
  }
});

Vue.component('modal', {
  props: ['items', 'eventName'],
  data() {
    return {
      shown: false,
    }
  },
  mounted() {
    EventBus.$on('showModal', this.show);
  },
  template: `<div v-if="shown">
  <ul v-if="items.length>0">
    <li v-for="item in items">
      {{ item }} <button @click="emitEvent(item)">Remove</button>
    </li>
  </ul>
  <em v-else>empty</em>
</div>`,
  methods: {
    show(items, callback) {
      this.shown = true;
    },
    emitEvent(item) {
      this.$emit(this.eventName, item);
    }
  }
});

var app = new Vue({
  el: '#app'
})
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <comp></comp>
</div>

以上是关于Vue.js计算属性在通过事件时失去其反应性的主要内容,如果未能解决你的问题,请参考以下文章

如何更新反应性对象(和一般属性)?

Vue 计算属性不会在更新其反应性依赖项时更新

Vue 是不是支持 Map 和 Set 数据类型的反应性?

Vue.js 反应性如何在幕后工作?

在 Vue.js 中反应性地重新填充指令数组

Vue.js 3:当 `data()` 转换为 `setup()` 时,`v-model` 失去响应性