Vue 3 组合 API:计算过滤数组的问题

Vue 3 composition API: issue with computed to filter array

我正在学习 Vue 3 组合 API 我做了一个简单的 post 列表(简单地创建一个 post 的数组每个 posts) 的 setup() 带有标签,如下面的第一个代码部分。 然后我有一个组件 PostList 来显示那些 posts 和一个 TagCloud 来显示所有现有的标签。 我正在尝试通过单击标签来过滤 posts。

以下是 3 个元素协同工作的结果,有人能找出问题所在吗?

主视图

<template>
  <div class="home">
    <h1>Today I learned (TIL)</h1>
    <div class="layout">
      <TagCloud :tils="tils" @tag="filterTils" />
      <PostList :tils="filterTils" />
    </div>
  </div>
</template>

<script>
import PostList from "../components/PostList.vue";
import TagCloud from "../components/TagCloud.vue";
import { ref, computed } from "vue";

export default {
  name: "Til",
  components: { PostList, TagCloud },
  setup() {
    const tils = ref([
      {
        title: "Symbol#to_proc conversion",
        description: "map(&:to_i) is exactly the same as map { |x| x.to_i }",
        tags: ["ruby"],
      },
      {
        title: "Symbol#to_proc conversion",
        description:
          "Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum ",
        tags: ["lorem"],
      },
      {
        title: "Symbol#to_proc conversion",
        description: "map(&:to_i) is exactly the same as map { |x| x.to_i }",
        tags: ["ruby"],
      },
    ]);

    const filterTils = computed((tag) => {
      return tils.value.filter((til) => til.tags.includes(tag));
    });
    
    return { tils, filterTils };
  }
};
</script>

标签云组件

<template>
  <div class="tag-cloud">
    <h3>Tags</h3>
    <div v-for="tag in tags" :key="tag">
      <div @click="filterTag(tag)" class="uniq-tag">#{{ tag }}</div>
    </div>
  </div>
</template>

<script>
import useTags from "../composables/useTags";

export default {
  props: ["tils"],
  setup(props, context) {
    const { tags } = useTags(props.tils);
    const filterTag = (tag) => {
      context.emit("tag", tag);
    };
    return { tags, filterTag };
  },
};
</script>

post 列表组件

<template>
  <div class="til-list">
    <div v-for="til in tils" :key="til">
      <div class="til">
        <h3>{{ til.title }}</h3>
        <p>{{ til.description }}</p>
        <span v-for="tag in til.tags" :key="tag"> #{{ tag }} </span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ["tils"],
};
</script>

您正在尝试将 filterTils 用作 tag 事件的事件处理程序,以及 return 过滤结果的计算道具,但是这两件事应该是分开的。

另请注意,用 computed() 包装方法不一定 return 另一种方法。在 filterTils 的情况下,它 return 是一个 Array

解决方案

这是解决问题的一种方法:

  1. 添加一个 ref 来保存当前选择的标签:

    import { ref } from 'vue'
    
    export default {
      setup() {
        const currentTag = ref()
      }
    }
    
  2. 更改 @tag 事件处理程序以复制当前标签:

    <template>
      <TagCloud @tag="onTag" />
    </template>
    
    <script>
    export default {
      setup() {
        return {
          onTag: tag => currentTag.value = tag
        }
      }
    }
    </script>
    
  3. 更新filterTils以根据当前标签计算筛选结果:

    const filterTils = computed(() => {
      return currentTag.value
        ? tils.value.filter(til => til.tags.includes(currentTag.value))
        : tils.value;
    })
    

demo