问题
I have a single file component like this:
<template>
<div>
<template v-if="offers.length > 3">
<a href="#">View all offers here</a>
</template>
<template v-else-if="offers.length > 1">
<offer v-for="offer in offers" :data="offer"></offer>
</template>
<template v-else-if="offers.length == 1">
<offer :title="The offer" :data="offers[0]"></offer>
</template>
</div>
</template>
Based on the number of offers
, I choose how many to render.
Question: How do I efficiently get/count the number of <offer>
components? I also need that number to be reactive.
回答1:
There's no clean way how.
You could count the children of the current instance that are of a specific type. But you would have to call the "recount" logic on update
hook (as well as mounted
).
Example:
Vue.component('offer', {
name: 'Offer',
template: '<span> offer </span>'
})
new Vue({
el: '#app',
data: {
offers: [1, 2],
offerCount: 0
},
methods: {
updateOfferCount() {
this.offerCount = this.$children.filter(child => child.constructor.options.name === 'Offer').length;
}
},
updated() {
this.updateOfferCount()
},
mounted() {
this.updateOfferCount()
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div>
<template v-if="offers.length > 3">
<a href="#">View all offers here</a>
</template>
<template v-else-if="offers.length > 1">
<offer v-for="offer in offers" :data="offer"></offer>
</template>
<template v-else-if="offers.length == 1">
<offer :data="offers[0]"></offer>
</template>
</div>
<br>
<button @click="offers.push(123)">Add Offer</button> offerCount: {{ offerCount }}
</div>
回答2:
I'm answering this based solely on the idea that you want to count instantiations and destructions of Offer
components. I'm not sure why you don't just count offers.length
. Maybe other things can trigger instantiations.
Have the component emit events on creation and destruction and have the parent track accordingly.
Alternatively (and maybe overkill) you could use Vuex and create a store that the Offer
commits to on creation and destruction. This means that you don't have to manually attach @offer-created/destroyed
directives every time you put an <offer>
in your markup.
Both methods are included in the following example:
const store = new Vuex.Store({
strict: true,
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
}
});
const Offer = {
props: ["data"],
template: "<div>{{data.name}}</div>",
created() {
console.log("Created");
this.$emit("offer-created");
this.$store.commit("increment");
},
destroyed() {
console.log("Destroyed");
this.$emit("offer-destroyed");
this.$store.commit("decrement");
}
};
const app = new Vue({
el: "#app",
store,
components: {
offer: Offer
},
data() {
return {
offers: [],
offerCount: 0
};
},
computed: {
offerCountFromStore() {
return this.$store.state.count;
}
},
methods: {
offerCreated() {
this.offerCount++;
},
offerDestroyed() {
this.offerCount--;
},
addOffer() {
this.offers.push({
name: `Item: ${this.offers.length}`
});
},
removeOffer() {
this.offers.pop();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script>
<div id="app">
<div>Offer instances: {{offerCount}}</div>
<div>Offer instances (from store): {{offerCountFromStore}}</div>
<div>
<div v-if="offers.length > 3">
<a href="#">View all offers here</a>
</div>
<div v-else-if="offers.length > 1">
<offer @offer-created="offerCreated" @offer-destroyed="offerDestroyed" v-for="offer in offers" :data="offer"></offer>
</div>
<div v-else-if="offers.length == 1">
<offer @offer-created="offerCreated" @offer-destroyed="offerDestroyed" :data="offers[0]"></offer>
</div>
</div>
<div>
<button @click.prevent="addOffer">Add</button>
<button @click.prevent="removeOffer">Remove</button>
</div>
</div>
The problem with trying to use $children is that it is, inherently, not reactive:
The direct child components of the current instance. Note there’s no order guarantee for
$children
, and it is not reactive. If you find yourself trying to use$children
for data binding, consider using an Array andv-for
to generate child components, and use the Array as the source of truth.
来源:https://stackoverflow.com/questions/50047639/count-the-occurrences-of-a-child-component