コンポーネント化 パート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
を使用して、コンポーネント間でデータをやり取りできるようにしましょう。
チャレンジ2
props
と emit
を使用して親子間でデータの受け渡しをできるようにしましょう:
TodoList.vue
でdefineProps
を使用して親からtodos
を受け取れるようにしましょうapp.vue
に<TodoList />
を配置して、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)を通じて状態管理が整理され、チーム開発や拡張がしやすくなる
もし行き詰まったら、以下のボタンをクリックして解答を見ることができます。
コンポーネント化され、メンテナンスしやすいスッキリした構造になりました!