はじめに
v-modelをVue.jsの基礎から理解します。便利なv-modelですが、曖昧な理解になりがちだったため、今回の記事を書いています。
この記事の対象者
- Vue.jsに入門してチュートリアルを一通り進めた人
-
v-modelが具体的に何をしているのか理解したい人
v-modelとは?
v-modelとは、双方向バインディングを実現する構文です。例えば、<input>を使ってフォームを設置する場面を考えます。
<script setup>
import { ref } from 'vue'
const name = ref('')
</script>
<template>
<div>
<input v-model="name" />
<p>入力された名前: {{ name }}</p>
</div>
</template>
フォームに文字を入力すると、すぐに「入力された名前:」の右に内容が表示されます。双方向バインディングとして、
- フォームの値を変更するとJavaScript側の値
nameが変わる - JavaScript側の値
nameが変わると、画面が変わる
が起きています。
もし、v-modelを使わずに書くと?
v-modelは双方向バインディングを簡潔に書くための構文であるため、v-modelを使わずに双方向バインディングを実現できます。
<script setup>
import { ref } from "vue";
const name = ref("");
</script>
<template>
<input :value="name" @input="name = $event.target.value" />
<p>入力された名前: {{ name }}</p>
</template>
v-modelが内部で行っている処理のイメージをつかめます。
-
@input="name = $event.target.value"でJavaScriptの値を更新しています -
:value="name"でJavaScriptの値を画面に表示しています
子コンポーネントとの双方向バインディングにも使える
v-modelは子コンポーネントとの双方向バインディングに使うことができます。
<script setup>
import { ref } from "vue";
import Child from "@/components/Child.vue";
const count = ref(0);
</script>
<template>
<Child v-model="count" />
</template>
<script setup>
const model = defineModel();
function update() {
model.value++;
}
</script>
<template>
<p>{{ model }}</p>
<button @click="update">カウントを増やす</button>
</template>
子コンポーネントでは、defineModel()を使っています。
defineModel()を使うことで、v-modelで必要となるmodelValueの受け取りとupdate:modelValueイベントの通知を簡潔に書くことができます。
v-modelとdefineModel()を使わずに書くと、次のようなコードになります。
<script setup>
import { ref } from "vue";
import Child from "@/components/Child.vue";
const count = ref(0);
</script>
<template>
<Child :modelValue="count" @update:modelValue="count = $event" />
</template>
<script setup>
const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
function update() {
emit("update:modelValue", props.modelValue + 1);
}
</script>
<template>
<p>{{ props.modelValue }}</p>
<button @click="update">カウントを増やす</button>
</template>
個人的に、<input>などのフォーム要素に対する双方向バインディングはすぐにイメージできましたが、子コンポーネントとの双方向バインディングは少しイメージしにくいと感じていました。
しかし、親子コンポーネント間でも、
- 親コンポーネントはJavaScriptの値を子コンポーネントに渡す
- 子コンポーネントで値が変更されたら、変更後の値を親コンポーネントに通知し、親コンポーネント側の値を更新する
という処理を行っています。これは、フォーム要素に対する「値を渡す」「変更後の値を受け取ってJavaScript側の値を更新する」という双方向バインディングと、同じ構造だと理解できました。


