Vue.js 计算属性在传递事件时失去反应性 [英] Vue.js computed property loses its reactivity when passed through an event

查看:28
本文介绍了Vue.js 计算属性在传递事件时失去反应性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的主应用程序中有一个 Modal 组件,每当必须显示模态时,它都会通过事件获取传递的内容.模态内容始终是一个列表,其中包含与每个项目相关联的操作,例如选择"或删除":

Vue.component('modal', {数据() {返回 {显示:假,项目: [],回调:()=>{}}},安装(){EventBus.$on('showModal', this.show);},模板:`<ul v-if="shown"><li v-for="项目中的项目">{{ item }} <button @click="callback(item)">移除</button></ul>`,方法: {显示(项目,回调){this.shown = true;this.items = 物品;this.callback = 回调;}}});

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

Vue.component('comp', {数据() {返回 {obj: {a: 'foo', b: 'bar'}}},计算:{objKeys() {返回 Object.keys(this.obj);}},模板:`

<button @click="showModal">显示模态</button><模态></模态></div>`,方法: {删除(名称){this.$delete(this.obj, name);},显示模态(){EventBus.$emit('showModal', this.objKeys, this.remove);}}});

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

我认为这是一个错误 - Vue 不应该记住 objKeys 用于在 Modal 中进行渲染并更新它吗?(将 obj 的更改转发到 objKeys 有效.)如果没有,我哪里出错了,我怎样才能达到我想要的结果?

解决方案

您拥有使用自己的 items 副本的模式:

 模板:`
    <li v-for="项目中的项目">{{ item }} <button @click="callback(item)">移除</button></ul>`,方法: {显示(项目,回调){this.shown = true;this.items = 物品;this.callback = 回调;}}

在调用 show 时复制一次,您复制的只是您发出 showModal 事件时计算的值.show 接收的不是计算的,它分配的也不是计算的.这只是一个价值.

如果在你的代码中的任何地方,你做了一个像

someDataItem = someComputed;

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

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

const EventBus = new Vue();Vue.component('comp', {数据() {返回 {对象:{一个:'富',乙:'酒吧'}}},计算:{objKeys() {返回 Object.keys(this.obj);}},模板:`

<div>整个对象:{{ obj }}</div><div>只是键:{{ objKeys }}</div><button @click="remove('a')">删除一个</button><button @click="remove('b')">移除 b</button><button @click="showModal">显示模态</button><模态></模态></div>`,方法: {删除(名称){this.$delete(this.obj, name);},显示模态(){EventBus.$emit('showModal', () => this.objKeys, this.remove);}}});Vue.component('modal', {数据() {返回 {显示:假,获取项目:空,回调:() =>{}}},安装(){EventBus.$on('showModal', this.show);},模板:`<div v-if="shown"><ul v-if="items.length>0"><li v-for="项目中的项目">{{ item }} <button @click="callback(item)">移除</button><em v-else>空</em></div>`,计算:{项目() {返回 this.getItems &&this.getItems();}},方法: {显示(获取项目,回调){this.shown = true;this.getItems = getItems;this.callback = 回调;}}});var app = new Vue({el: '#app'})

<script src="//unpkg.com/vue@latest/dist/vue.js"></script><div id="应用程序"><comp></comp>

I have a Modal component in my main app that gets passed content via an event whenever a modal has to be shown. Modal content is always a list with an action associated with each item, like "select" or "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;
    }
  }
});

Sadly, when passing a computed property to that modal like in the component below, the reactive link gets broken -> if the action is "remove", the list is not updated.

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);
    }
  }
});

See the minimal use case in this fiddle: https://jsfiddle.net/christophfriedrich/cm778wgj/14/

I think this is a bug - shouldn't Vue remember that objKeys is used for rendering in Modal and update it? (The forwarding of the change of obj to objKeys works.) If not, what am I getting wrong and how could I achieve my desired result?

解决方案

You have the modal working with its own copy of 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;
    }
  }

That copy is made once, upon the call to show, and what you are copying is just the value of the computed at the time you emit the showModal event. What show receives is not a computed, and what it assigns is not a computed. It's just a value.

If, anywhere in your code, you made an assignment like

someDataItem = someComputed;

the data item would not be a functional copy of the computed, it would be a snapshot of its value at the time of the assignment. This is why copying values around in Vue is a bad practice: they don't automatically stay in sync.

Instead of copying values around, you can pass a function that returns the value of interest; effectively a get function. For syntactic clarity, you can make a computed based on that function. Then your code becomes

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>

这篇关于Vue.js 计算属性在传递事件时失去反应性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆