1. はじめに
現在、Vue2 から Vue3 へのマイグレーションに携わる中で、
「そもそも props とは?emits とは?」といった基礎でつまずいてしまいました。
そこで今回は、v-model
を使用したモーダルのコード例をもとに、流れを再確認してみようと思います。
本記事では、従来のOptions API で書いたコードを例に紹介します。
ここでは、親子間での props
の受け渡しと、子コンポーネントから親コンポーネントへの emit
によるイベント発火を、具体的なコード例を通してまとめます。
2. コンポーネントの構成
今回の例は 2 つのコンポーネントから成り立っています。
-
親コンポーネント
ボタンをクリックするとvisible
がtrue
になり、その状態が子コンポーネントへ渡す -
子コンポーネント(モーダル)
親から渡されたvisible
をもとにモーダルを表示し、モーダル内部でのクローズ操作によって親へ状態変更を通知する
親コンポーネントのコード例
<template>
<!-- ボタンをクリックすると、visible が true に更新され、子に伝わる -->
<button @click="visible = true">Open Modal</button>
<ChildComponent v-model:visible="visible" />
</template>
<script>
export default {
data() {
return {
// モーダルの表示/非表示管理
visible: false,
}
},
}
</script>
子コンポーネントのコード例
<template>
<div v-if="localVisible" class="modal-overlay">
<div class="modal-content">
<p>Modal Content Here</p>
<!-- クローズボタンでモーダルを閉じる -->
<button @click="closeModal">Close</button>
</div>
</div>
</template>
<script>
export default {
props: {
// 親コンポーネントから受け取る状態
visible: {
type: Boolean,
default: false,
},
},
emits: ['update:visible'],
computed: {
// computed プロパティで props をラップして双方向データバインディングを実現
localVisible: {
get() {
return this.visible;
},
set(value) {
this.$emit('update:visible', value);
},
},
},
methods: {
closeModal() {
// クローズボタン押下時、親にモーダルを閉じるよう通知
this.$emit('update:visible', false);
},
},
}
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #fff;
padding: 20px;
border-radius: 4px;
min-width: 300px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
</style>
3. Props の受け渡し
親から子へのデータ伝達
-
親コンポーネント側
data
オプションで定義したvisible
を管理し、ボタンがクリックされるとvisible
の値をtrue
に更新 -
子コンポーネント側
props
オプションでvisible
を受け取ることで、親コンポーネントの状態を参照できるようにする
4. v-model を用いた双方向バインディングの実現
computed プロパティでのラッピング
- 子コンポーネントでは、親の
visible
を直接変更できないため、localVisible
という computed プロパティを定義し、以下のように動作させています:-
Getter:
親から渡されたvisible
をそのまま返し、表示条件(v-if
)などで使用 -
Setter:
localVisible
に値が設定されると、this.$emit('update:visible', value)
を実行し、親に変更を通知
-
Getter:
こうすることで、<ChildComponent v-model:visible="visible" />
と書いた際に、Vue は内部的に update:visible
イベントを監視し、親の visible
を更新します。
5. Emit によるイベント発火の流れ
子から親への通知
-
閉じる操作の場合:
子コンポーネント内の「Close」ボタンがクリックされると、closeModal
メソッドが呼び出され、その中でthis.$emit('update:visible', false)
を実行します
これにより、親コンポーネントに対して「モーダルを閉じる」ように通知します
親コンポーネントでの反映
- 親は
v-model:visible
を通じて子のupdate:visible
イベントを受け取っているため、false
が emit されると自動的にvisible
が更新され、モーダルが非表示となります
6. まとめ
-
データの一方向伝播:
親から子へはprops
を使って一方向に状態(この例ではvisible
)が渡される -
子から親への通知:
子コンポーネントは、$emit
でイベント(ここではupdate:visible
)を発火し、親に状態変更を通知する -
双方向バインディングの実現:
computed プロパティの getter / setter を設定することで、v-model
による双方向バインディングが実現
7.参考