〇背景
Vue を触っていると頻繁に出てくる v-model
。
フォーム入力やチェックボックスなどでよく使われるが、裏側で何が起きているのかが分からなかったため調査することにした。
〇今回のコード例
一番シンプルな使い方
<script setup>
import { ref } from 'vue'
const message = ref('こんにちは!')
</script>
<template>
<input v-model="message" />
<p>入力値:{{ message }}</p>
</template>
〇調べる前の自分の認識
- 「なんか
input
と一緒によく出てくる」 - 「双方向で値が変わってるっぽいが、props や emit との関係は不明」
- 「魔法みたいな記法?」というイメージ
〇調べた結果
-
v-model
は props + emit の省略形 - Vue2:
value
prop とinput
イベント - Vue3:
modelValue
prop とupdate:modelValue
イベント - 自作コンポーネントでも
v-model
を使えるようにできる - 複数の v-model(例:
v-model:title
)もサポート
〇動作解説(図解やコメント付きコード)
シンプルな v-model
<input v-model="message" />
⬇ Vue が裏で展開するとこうなる
<input
:value="message"
@input="message = $event.target.value"
/>
自作コンポーネント版
子コンポーネント
<script setup>
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits<{ (e:'update:modelValue', v:string): void }>()
</script>
<template>
<input :value="props.modelValue"
@input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" />
</template>
親コンポーネント
<MyInput v-model="message" />
図解イメージ:
[Parent] message
↓ (props: modelValue)
[Child] input :value
↑ (emit: update:modelValue)
〇実務での注意点
- v-model = 双方向 なので、データの流れが複雑になりすぎないよう注意。
- コンポーネントの責務分離を意識し、状態は基本「親」、UI 部品は「子」に任せる。
- 複数の v-model を使う場合は、命名規則を揃えて混乱を防ぐ。
- バリデーションや重い処理は
@input
で直接やらずに debounce などを検討。
〇まとめ・所感
-
v-model
は 「props で値を受け取り、emit で更新を返す」 という定番パターンの糖衣構文だった。 - 特にフォームや入力部品ではほぼ必須で、これを理解すると props/emit の使い分けも整理できる。
- 実装の魔法感はなくなり、「Vue が裏で props と emit を自動で書いてくれているだけ」と分かってスッキリした。
👉 次のステップとしては、複数の v-model を持つコンポーネント(例:v-model:title
と v-model:content
) を試すと、さらに応用理解が深まりますよ。
変数(状態)と要素(UI)
「双方向」とは、
- 親のデータが UI に反映される
- UI の操作が親のデータに反映される