# カテゴリー一覧を表示するVue.jsアプリの作成

2024-02-11 14:04:00

今回は、Vue.jsを使用して、カテゴリーごとに記事一覧を表示するアプリを作成します。具体的には、VuePressを使用してMarkdownファイルからカテゴリーを取得し、カテゴリーごとに記事一覧を表示します。

# 1. enhanceApp.jsの作成

まずは、VuePressのenhanceApp.jsを使用して、Markdownファイルからカテゴリーを取得する処理を行います。この処理は、Vueのmix-inを使用して全てのページで共通の処理を行います。

export default ({ Vue }) => {
  Vue.mixin({
    computed: {
      categories() {
        const categories = new Set(); 

        this.$site.pages.forEach(page => {
          if (page.frontmatter.category) {
            categories.add(page.frontmatter.category);
          }
        });

        return Array.from(categories);
      }
    }
  });
};

このコードでは、全てのページのMarkdownファイルからカテゴリーを取得し、重複のない一意のカテゴリー一覧を作成しています。

# 2. Category.vueの作成

次に、カテゴリーごとに記事一覧を表示するVueコンポーネントを作成します。これには、Category.vueを使用します。

<template>
  <div>
    <div v-for="category in categories" :key="category">
      <h3>{{ category }} の記事一覧</h3>
      <ul>
        <li v-for="post in filteredPosts(category)" :key="post.title">
          <!-- post.title が存在する場合のみ表示 -->
          <router-link v-if="post.frontmatter.title" :to="post.path">{{ post.frontmatter.title }}</router-link>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      categories: [], 
    };
  },
  computed: {
    filteredPosts() {
      return (category) => {
        if (!this.$site || !this.$site.pages) {
          return [];
        }

        return this.$site.pages
          .filter(page => page.frontmatter.category === category && page.frontmatter.title)
          .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
      };
    },
  },
  created() {
    this.categories = this.$store.state.categories;
  }
};
</script>

このコンポーネントでは、カテゴリーごとにフィルタリングされた記事一覧を表示する処理を行います。createdフック内でVuexストアからカテゴリーを取得しています。

# 3. Category.vue内で直接カテゴリーを定義したい場合

<template>
  <div>
    <div v-for="category in categories" :key="category">
      <h3>{{ category }} の記事一覧</h3>
      <ul>
        <li v-for="post in filteredPosts(category)" :key="post.title">
          <!-- post.title が存在する場合のみ表示 -->
          <router-link v-if="post.frontmatter.title" :to="post.path">{{ post.frontmatter.title }}</router-link>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      categories: ["Vue.js", "React", "Angular"], // カテゴリーを直接定義
    };
  },
  computed: {
    filteredPosts() {
      return (category) => {
        if (!this.$site || !this.$site.pages) {
          return [];
        }

        return this.$site.pages
          .filter(page => page.frontmatter.category === category && page.frontmatter.title)
          .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
      };
    },
  },
};
</script>