Vue.js 计算属性在传递事件时失去反应性 [英] Vue.js computed property loses its reactivity when passed through an event
问题描述
我的主应用程序中有一个 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屋!
查看全文