在此处参考实时演示代码:
https://codesandbox.io/s/vue-template-r26tg
假设我有一个Vuex商店,其中包含以下数据:
const store = new Vuex.Store({
state: {
categories: [
{
name: "Category A",
items: [{ name: "Item 1" }, { name: "Item 2" }, { name: "Item 3" }]
},
{
name: "Category B",
items: [{ name: "Item A" }, { name: "Item B" }, { name: "Item C" }]
},
{
name: "Category C",
items: [{ name: "Item !" }, { name: "Item @" }, { name: "Item #" }]
}
]
}
});
我设置了一个App.vue
,Category.vue
和Item.vue
,以使其呈现如下:
//App.vue
<template>
<div id="app">
<Category v-for="(category, index) in categories" :category="category" :key="index"/>
</div>
</template>
<script>
export default {
components: { Category },
computed: {
...mapState(["categories"])
}
};
</script>
//Category.vue
<template>
<div class="category">
<div class="header">{{ category.name }}</div>
<Item v-for="(item, index) in category.items" :item="item" :key="index"/>
</div>
</template>
<script>
export default {
components: { Item },
props: {
category: { type: Object, required: true }
}
};
</script>
//Item.vue
<template>
<div class="item">
<div class="name">{{ item.name }}</div>
<div class="delete" @click="onDelete">✖</div>
</div>
</template>
<script>
export default {
props: {
item: { type: Object, required: true }
},
methods: {
onDelete() {
this.$store.commit("deleteItem", this.item);
}
}
};
</script>
换句话说,App.vue
从Vuex获取类别列表,然后将其作为每个类别的道具向下传递到Category.vue
,然后Category.vue
向下传递category.items
Item.vue
作为每个项目的道具。
当单击项目旁边的删除按钮时,我需要删除该项目:
但是,在Item.vue
级别,我只能访问item
,而不能访问category
。如果我将item
发送给Vuex,则无法得知它属于哪个category
。如何获得对category
的引用,以便可以使用Vuex从其中删除该项目?
我可以想到两种方式:
为每个category
向item
添加父引用。这是不希望的,不仅因为我必须处理item
数据,还因为它引入了一个循环引用,而我不想在应用程序的其他部分中处理它。
从Item.vue
到Category.vue
发出一个事件,并让Category.vue
处理Vuex调用以进行删除。这样一来,类别和要删除的项目就都知道了。
有没有更好的方法来处理这种删除?
答案 0 :(得分:1)
我强烈建议(2)。通常,如果您可以创建一个接受道具并发出事件的组件,而没有其他副作用(API调用,Vuex突变等),则通常是正确的路径。在这种情况下,您甚至可以将事件完全推送回父级的父级。
共享状态(Vuex)真正帮助的地方是,当您在DOM树中有两个或多个彼此远离的组件时。例如。想象一个带有总项目数的标题。这种程度的空间分隔可能存在于您的应用中,但在这个简单的示例中却没有。
在此处发出事件的另一个好处是,您可以更轻松地使用storybook之类的工具,而不必处理任何Vuex解决方法。
答案 1 :(得分:0)
我个人希望使用2(发出从Item.vue
到Category.vue
的事件),但是,由于您询问了可能性,因此还有第三种方法:传递< strong>回调功能 。
示例:
Category.vue
:
<template>
<div class="category">
<div class="header">{{ category.name }}</div>
<Item v-for="(item, index) in category.items" :item="item" :key="index"
:on-delete="deleteItem"/>
</div>
</template>
<script>
// ...
export default {
// ...
methods: {
deleteItem(i) {
console.log('cat', this.category.name, 'item', i)
//this.$store.commit("deleteItem", this.item);
}
}
};
</script>
Item.vue
:
<template>
<div class="item">
<div class="name">{{ item.name }}</div>
<div class="delete" @click="() => onDelete(this.item)">✖</div>
</div>
</template>
<script>
export default {
props: {
item: { type: Object, required: true },
onDelete: { type: Function }
},
};
</script>
Updated sandbox here。请注意,在这种情况下,回调为onDelete
。
如果这是React,那么回调肯定是更惯用的方式。如前所述,在Vue中,我主张在孩子中发出事件并在父母中处理它(使用v-on
)。