Vue 3 コンポーネントの基本|作り方・登録・slotの使い方

Vue 3 コンポーネントの基本とは

コンポーネントは、画面を作る「部品」です。ボタン、カード、リストの1行のように、見た目と動きをひとまとまりにして、何度でも使い回せるようにしたものです。公式ドキュメントでは、コンポーネントによって UI を独立した再利用可能な部品に分割し、それぞれを切り離して考えられるようになると説明しています。

この記事では、.vue ファイル(単一ファイルコンポーネント)で部品を作る方法、部品を使えるようにする「登録」、親子のやりとり(props と emit)、中身を差し込む slot までを、実際にビルドして動かした例で説明します。

基本(作る・登録する・使う)

手順 書き方 ポイント
作る TodoItem.vue などのファイルを作る ファイル名は PascalCase(単語の頭を大文字)が一般的
登録する <script setup> で import TodoItem from '…' import するだけで、そのファイルのテンプレートで使える(ローカル登録)
使う <TodoItem /> .vue の中では自己終了タグも使える

登録には2種類あります。

登録 書き方 特徴
ローカル登録 使うファイルで import する どこで使っているかが分かりやすい。基本はこれ
グローバル登録 app.component('名前', 部品) アプリ全体で使える。使っていなくてもビルドから外れにくい

公式ドキュメントは、グローバル登録には、使われていない部品がビルドから取り除かれにくい、どこで使われているか分かりにくい、といった欠点があると説明しています。

サンプルコード(実行結果つき)

src/components/TodoItem.vue(子の部品)

<script setup>
defineProps({
  text: { type: String, required: true }
})
const emit = defineEmits(['remove'])
</script>

<template>
  <li>
    {{ text }}
    <button type="button" @click="emit('remove')">削除</button>
  </li>
</template>

src/components/BaseCard.vue(中身を差し込む枠)

<template>
  <section class="card">
    <slot>(中身がありません)</slot>
  </section>
</template>

<style scoped>
.card {
  border: 1px solid #cccccc;
  padding: 8px 16px;
}
</style>

親の部品

<script setup>
import { ref } from 'vue'
import BaseCard from '@/components/BaseCard.vue'
import TodoItem from '@/components/TodoItem.vue'

const todos = ref([
  { id: 1, text: '牛乳を買う' },
  { id: 2, text: '本を返す' }
])
function removeTodo(id) {
  todos.value = todos.value.filter((t) => t.id !== id)
}
</script>

<template>
  <BaseCard>
    <h2>やること({{ todos.length }}件)</h2>
    <ul>
      <TodoItem v-for="t in todos" :key="t.id" :text="t.text" @remove="removeTodo(t.id)" />
    </ul>
  </BaseCard>
  <BaseCard />
</template>

動作確認(検証環境と結果)

  • Vue 3.5.43、Vite 8.3.3(npm create vue@latest で作成)、Microsoft Edge、2026年10月。npm run build のあと npm run preview で確認しました。
  • 最初は「やること(2件)」「牛乳を買う 削除」「本を返す 削除」と表示されました。
  • 「牛乳を買う」の削除を押すと「やること(1件)」になり、「本を返す」だけが残りました。
  • 中身を書かずに置いた <BaseCard /> には、slot の初期値の「(中身がありません)」が表示されました。

Vue 3 コンポーネントの基本の注意点

  • props は親から子への一方通行です。 子は受け取った props を書き換えません。変えたいときは、emit で親に知らせ、データを持つ親が変えます。
  • slot の初期値: <slot> の中に書いた内容は、親が何も渡さなかったときだけ表示されます。
  • HTML に直接書くテンプレート(CDN 版)では書き方が違います。 部品名を todo-item のような小文字のハイフン区切りで書き、<todo-item /> のような自己終了タグは使えません。

Vue 3 コンポーネントの基本でよくあるミス

  • import を書き忘れて、部品が表示されない(コンソールに警告が出ます)。
  • v-for で部品を繰り返すときに :key を付け忘れる。
  • 1つの部品に機能を詰め込みすぎ、逆に分けすぎて、どこに何があるか分からなくなる。

Vue 3 コンポーネントの基本のチェックリスト

  • [ ] 部品を .vue ファイルに分けた
  • [ ] 使うファイルで import してローカル登録した
  • [ ] defineProps で受け取る値を決めた
  • [ ] 子から親へは defineEmits で知らせた
  • [ ] 枠だけの部品には slot を使った

Vue 3 コンポーネントの基本のFAQ(よくある質問)

Q. どのくらいの大きさで部品に分ければいいですか?

A. 2回以上使うもの、またはファイルが長くなって読みにくくなったものを分ける、という目安が分かりやすいです。

Q. defineProps は import しなくていいのですか?

A. defineProps と defineEmits は <script setup> の中で使えるコンパイラーマクロで、import は不要です。

Q. slot は複数置けますか?

A. 名前付きスロット(<slot name="header">)を使うと、場所ごとに別の中身を差し込めます。

筆者の見解(Vue 3 コンポーネントの基本)

コンポーネントは Vue の中心ですが、最初から細かく分けようとすると、かえって迷いやすいと感じます。私は、まず1つのファイルで画面を作り、同じ形が2回出てきた所から部品にする進め方を勧めます。そのうえで「データを持つのは親、子は表示と知らせるだけ」という役割分担を守れば、部品が増えても見通しが保てると考えます。

Vue 3 コンポーネントの基本の関連項目

出典(一次情報)

タイトルとURLをコピーしました