コンポーネント化 パート2
コンポーネント間のデータの受け渡し
Vue コンポーネント間でデータを受け渡しする基本的な方法として、props と emit を使用します。
Props
親コンポーネントから子コンポーネントにデータを渡すための方法です。
まずは子コンポーネント側で defineProps マクロを使用し、受け取りたいデータを定義します。
<!-- Child.vue -->
<script setup lang="ts">
defineProps<{ message: string }>()
</script>
次に親コンポーネント側で、子コンポーネントにデータを渡すために v-bind ディレクティブを使用します。:props名="データ" という形式で、子コンポーネントにデータを渡すことができます。
<!-- Parent.vue -->
<template>
<Child :message="message" />
</template>
また、props 名とデータの変数名が同名の場合は省略記法を使うことができます。
<!-- Parent.vue -->
<template>
<Child :message />
</template>
Emit
子コンポーネントから親コンポーネントにイベントを発火するための方法です。
まずは子コンポーネント側で defineEmits マクロを使用し、発火したいイベントを定義します。
emit 関数を用いて、イベントを発火することができます。
<!-- Child.vue -->
<script setup lang="ts">
const emit = defineEmits<{ sendMessage: [] }>()
</script>
<template>
<button type="button" @click="emit('sendMessage')">
Click me
</button>
</template>
発火されたイベントは親コンポーネント側で v-on ディレクティブを使用して受け取ることができます。
<!-- Parent.vue -->
<script setup lang="ts">
function handleSendMessage() {
console.log('Message sent!')
}
</script>
<template>
<Child @send-message="handleSendMessage" />
</template>
以下のように、イベント発火時に子コンポーネントからデータを受け渡すこともできます。
<!-- Child.vue -->
<script setup lang="ts">
const emit = defineEmits<{ sendMessage: [string] }>()
</script>
<template>
<button type="button" @click="emit('sendMessage', 'Hello, Vue!')">
Click me
</button>
</template>
<!-- Parent.vue -->
<script setup lang="ts">
function handleSendMessage(message: string) {
console.log(message) // Hello, Vue!
}
</script>
<template>
<Child @send-message="handleSendMessage" />
</template>
それぞれの詳しい API ドキュメントから確認することができます。
現在の実装の課題
app.vueに定義されたtodosにTodoList.vueからアクセスすることができていません。
props と emit を使用して、コンポーネント間でデータをやり取りできるようにしましょう。
チャレンジ
props と emit を使用して親子間でデータの受け渡しをできるようにしましょう:
TodoList.vueでdefinePropsを使用して親からtodosを受け取れるようにしましょうapp.vueの<TodoList />でv-bindディレクティブを使用して親コンポーネントからtodosを渡してみましょうTodoList.vueでdefineEmitsを使用してアイコンのクリックイベントを親に伝えられるようにしましょうapp.vueでTodoList.vueから受け取ったイベントを利用してupdateDoneを実行しましょう
参考実装:
<script setup lang="ts">
/**
* Props
*/
defineProps<{
todos: Todo[]
}>()
/*
* Emit
*/
const emit = defineEmits<{
updateDone: [number, boolean]
}>()
</script>
実装後の効果
コンポーネント化すると:
- 関心ごと(表示とロジック)が分割され、コードの見通しが良くなる
- 複数のコンポーネントで同じUIパーツを再利用できるようになる
- 親子間のデータ受け渡し(props・emit)を通じて状態管理が整理され、チーム開発や拡張がしやすくなる
もし行き詰まったら、以下のボタンをクリックして解答を見ることができます。
コンポーネント化され、メンテナンスしやすいスッキリした構造になりました!