【フロントエンド入門16】カスタムコンポーネントの作成|再利用可能なUIパーツを構築

Webアプリケーションの開発では、同じようなUIパーツを何度も使うことがよくあります。そこで役立つのがカスタムコンポーネントです。Vue.jsのカスタムコンポーネントを使えば、コードの再利用性が高まり、保守性や拡張性も向上します。本記事では、カスタムコンポーネントの作成方法と実践的な使い方について初心者向けにわかりやすく解説します。

カスタムコンポーネントとは?

コンポーネントの概要

コンポーネントとは、再利用可能なUIパーツを指します。コンポーネントごとにHTML、CSS、JavaScriptを1つのまとまった単位として扱えるため、UIの構築がシンプルで効率的になります。

カスタムコンポーネントのメリット

  • コードの再利用:同じUIパーツを複数箇所で再利用できるため、コードの重複を減らせます。
  • 保守性の向上:1つのコンポーネントを修正すれば、すべての利用箇所に反映されます。
  • モジュール化:大規模なアプリでも各コンポーネントが独立しているため、管理が簡単です。

基本的なカスタムコンポーネントの作成

1. コンポーネントの作成

まずは、簡単なボタンコンポーネントを作成してみましょう。

src/components/MyButton.vue

<template>
  <button @click="handleClick" :class="buttonClass">
    {{ label }}
  </button>
</template>

<script>
export default {
  props: {
    label: {
      type: String,
      required: true
    },
    type: {
      type: String,
      default: 'primary'
    }
  },
  computed: {
    buttonClass() {
      return `btn-${this.type}`;
    }
  },
  methods: {
    handleClick() {
      this.$emit('click');
    }
  }
};
</script>

<style scoped>
.btn-primary {
  background-color: blue;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;
}

.btn-secondary {
  background-color: gray;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;
}
</style>

2. コンポーネントの登録と使用

作成したコンポーネントを親コンポーネントで使用するには、インポートして登録します。

src/App.vue

<template>
  <div>
    <h1>カスタムボタンの例</h1>
    <MyButton label="クリックしてね" type="primary" @click="onButtonClick" />
    <MyButton label="キャンセル" type="secondary" @click="onCancel" />
  </div>
</template>

<script>
import MyButton from './components/MyButton.vue';

export default {
  components: {
    MyButton
  },
  methods: {
    onButtonClick() {
      alert('ボタンがクリックされました!');
    },
    onCancel() {
      alert('キャンセルボタンが押されました');
    }
  }
};
</script>

この例では、カスタムボタンを複数作成し、それぞれ異なる動作を割り当てています。

Propsを使ったデータの受け渡し

Propsとは?

propsは親コンポーネントから子コンポーネントにデータを渡すための仕組みです。親コンポーネントが子コンポーネントにどのような情報を渡すかを定義し、動的なUIを構築できます。

例:プロンプトメッセージの表示

AlertMessage.vue

<template>
  <div class="alert" :class="type">
    {{ message }}
  </div>
</template>

<script>
export default {
  props: {
    message: {
      type: String,
      required: true
    },
    type: {
      type: String,
      default: 'info'
    }
  }
};
</script>

<style scoped>
.alert {
  padding: 10px;
  margin: 10px 0;
  border-radius: 5px;
}

.info {
  background-color: lightblue;
  color: black;
}

.warning {
  background-color: yellow;
  color: black;
}

.error {
  background-color: red;
  color: white;
}
</style>

使用例:

<AlertMessage message="これは情報メッセージです" type="info" />
<AlertMessage message="警告です!" type="warning" />
<AlertMessage message="エラーが発生しました" type="error" />

このように、親コンポーネントから異なるプロパティを渡すことで、メッセージの種類に応じたUIを表示できます。

イベントを使った親子コンポーネント間の通信

子コンポーネントから親コンポーネントへ通知する

子コンポーネントで発生したイベントを親コンポーネントに伝えるには、$emitメソッドを使用します。

例:削除ボタンの実装

DeleteButton.vue

<template>
  <button @click="$emit('delete')">削除</button>
</template>

使用例:

<template>
  <div>
    <h2>アイテム一覧</h2>
    <ul>
      <li v-for="(item, index) in items" :key="index">
        {{ item }} <DeleteButton @delete="removeItem(index)" />
      </li>
    </ul>
  </div>
</template>

<script>
import DeleteButton from './components/DeleteButton.vue';

export default {
  components: {
    DeleteButton
  },
  data() {
    return {
      items: ['アイテム1', 'アイテム2', 'アイテム3']
    };
  },
  methods: {
    removeItem(index) {
      this.items.splice(index, 1);
    }
  }
};
</script>

この例では、DeleteButtonコンポーネントがクリックされると、親コンポーネントに通知されてアイテムが削除されます。

スロットを使った柔軟なコンテンツの挿入

スロットとは?

スロットは、親コンポーネントから任意のコンテンツを子コンポーネントに挿入するための機能です。

例:カードコンポーネント

Card.vue

<template>
  <div class="card">
    <slot></slot>
  </div>
</template>

<style scoped>
.card {
  border: 1px solid #ccc;
  padding: 20px;
  border-radius: 5px;
  box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
}
</style>

使用例:

<Card>
  <h3>タイトル</h3>
  <p>このコンテンツは親コンポーネントから渡されています。</p>
</Card>

スロットを使うことで、汎用的なコンポーネントに柔軟な内容を挿入できます。

結論

カスタムコンポーネントを使えば、コードの再利用性を高めながら効率的にUIを構築することができます。本記事で紹介したprops、イベント、スロットの基本を理解することで、Vue.jsのコンポーネント設計がより柔軟になります。