【问题标题】:Count the occurrences of a child component计算子组件的出现次数
【发布时间】:2018-04-26 16:12:14
【问题描述】:

我有一个像这样的单个文件组件:

<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>

根据offers的数量,我选择渲染的数量。

问题:如何有效地获取/计算&lt;offer&gt; 组件的数量?我还需要这个数字是被动的。

【问题讨论】:

  • 这并不容易的事实应该让您怀疑自己是否做错了。如果你尝试将状态封装在 vue 组件中,你将过着悲惨的生活。 Vue 组件希望成为商店的无状态接口。优惠在橱窗范围内的商店中。 @DerekFulginiti 被否决的答案是正确的。让你的状态脱离你的组件,你的问题就会消失。坚持这样做,你的问题才刚刚开始。
  • return offers.length &gt; 3 ? 0 : offers.length 的计算属性不能工作吗?

标签: javascript vuejs2 vue-component


【解决方案1】:

没有干净的方法。

您可以计算当前实例属于特定类型的子代。但是您必须在 update 挂钩(以及 mounted)上调用“重新计数”逻辑。

例子:

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>

【讨论】:

  • 啊哈!我错过了mounted 的更新。你能解释一下为什么需要它吗?如果我在beforeUpdate 中这样做会更好,这样如果offerCount 影响其他组件的状态,它们可以在同一个渲染中更新吗?由于update 在渲染结束时被调用并更新offerCount 那么可能会导致一个新的?
  • 您需要在mounted 中运行,以便它第一次运行。 (而mounted 是第一次渲染后立即运行的钩子。)关于beforeUpdateupdated,在这种情况下,至少在这个演示中,这并不重要。总更新将最终相同。也许你可以在你的真实场景中做更多的测试。以下是我曾经测试过的内容:jsfiddle.net/acdcjunior/fx4sttft——不过,如果有疑问,绝对可以选择beforeUpdate,是的。
【解决方案2】:

我只是根据您想要计算Offer 组件的实例化和破坏的想法来回答这个问题。我不确定你为什么不只计算offers.length。也许其他事情可以触发实例化。

让组件在创建和销毁时发出事件,并相应地让父跟踪。

或者(也许是矫枉过正)你可以使用Vuex 并创建一个Offer 在创建和销毁时提交的存储。这意味着您不必每次在标记中添加 &lt;offer&gt; 时都手动附加 @offer-created/destroyed 指令。

这两种方法都包含在以下示例中:

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>

尝试使用$children 的问题在于,它本质上不是被动的:

当前实例的直接子组件。 注意没有 $children 的订单保证,并且它不是响应式的。如果你发现 您自己尝试使用 $children 进行数据绑定,请考虑使用 Array 和 v-for 生成子组件,并将 Array 用作 真相的源泉。

【讨论】:

  • 我不跟踪 offers.length,因为如果它超过 3,我不会创建 &lt;offer&gt; 组件,所以我要寻找的值是 0
  • 如果没有更好的选择,我会使用你的方法。您如何看待this.$children + 对父母的一些钩子?检查this.$children[i].$options._componentTag 的循环。我目前正在努力实现这一目标。
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 2015-06-18
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 2019-01-15
相关资源
最近更新 更多