
ブログをNext.js14(App Router)でリファクタリングしました
Vue3のカスタムコンポーネントにおけるv-modelの使用方法について解説します。また、Vue2ユーザのためにVue3での仕様変更についても併せて説明しています。Vue3での仕様変更によって、Vue2で動いていたコードが動かなくなります。例えばpropsのvalueがmodelValueに変わっていたり、emitの発火にupdateを記述するなど変更点が多いので、順を追って解説していきます。
目次
この章はVue2ユーザのみお読み下さい。
Vue3で始めてカスタムコンポーネントのv-modelを扱う方は、次の章へお進み下さい。
Vue3における変更点は次のとおりです。
以下の章でそれぞれの変更点についても解説します。
カスタムコンポーネントにおけるv-modelは、modelValueプロパティを渡してupdate:modelValueイベントを発火させるのと等価です。
<Child v-model="text"/>
<!-- 下と同じ意味 -->
<Child :modelValue="text" @update:modelValue="text = $event"/>
よって、子コンポーネント(カスタムコンポーネント)側ではmodelValueプロパティを受け取る準備と、update:modelValueイベントをemitさせる処理を記述すれば良いことになります。
したがって、子カスタムコンポーネント側のコードは次のようになります。
(以下では簡単な例として、カスタムコンポーネントのinputフォームのinputイベントを発火させています。)
・テンプレート
<template>
<input :value="modelValue" type="text" @input="onInputText" />
</template>
・スクリプト
<script lang="ts" setup>
const props = defineProps<{modelValue: string}>()
const emits = defineEmits<{(e: 'update:modelValue', text: string): void}>()
const onInputText = (e: Event) => {
const target = e.target as HTMLInputElement
emits('update:modelValue', target.value)
}
</script>
※注意
この記事では上のコードのようにscript setup構文で記述しています。script setup構文について詳しく知りたい方は次の記事を参照下さい。
Vue2はデフォルトでvalue
という名前のpropsが子コンポーネントに渡っていましたが、Vue3ではmodelValue
という名前に変更されています。
したがって、子コンポーネントではmodelValueというpropsを受け取る記述をします。
<script lang="ts" setup>
const props = defineProps<{modelValue: string}>()
</script>
Vue2の子コンポーネントで$emit('input', event.target.value)
などとしてイベントを送信していましたが、Vue3でこのコードは動かなくなります。
代わりにemit('update:modelValue', event.target.value)
とします。
デフォルトではmodelValueがプロパティに渡っていましたが、これは変更することが出来ます。
そのためにはv-modelの引数を使います。
その記述方法ですが、v-model:プロパティ名=値
のようにv-modelにコロンをつけて引数名を続けて書きます。
それに伴い、発火させるemitのイベント名もupdate:プロパティ名
のように変更します。
以上を踏まえて、textという名前のプロパティ名に変更する例を示します。
先程の例と同じではつまらないので、今回はchangeイベントを発生させています。
・親コンポーネント
<template>
<MyComponent v-model:text="data"/>
</template>
・子コンポーネントのテンプレート
<template>
<input :value="text" type="text" @change="onChangeText" />
</template>
・子コンポーネントのスクリプト
<script lang="ts" setup>
const props = defineProps<{text: string}>()
const emits = defineEmits<{(e: 'update:text', text: string): void}>()
const onChangeText = (e: Event) => {
const target = e.target as HTMLInputElement
emits('update:text', target.value)
}
</script>
Vue2では子コンポーネントにmodelオプションを用意し、紐づくプロパティやイベントを変更していました。しかしVue3ではこのオプションは削除されました。代わりにv-modelの引数を用いることになります。
前節で出てきた引数を使い分ければ、同時に複数のバインディングが可能になります。
次の例では、コンポーネントに2つv-modelを付けています。
・親コンポーネント
<template>
<Child
v-model:text="data1"
v-model="data2"
/>
</template>
子コンポーネントのテンプレート
<template>
<input type="text" :value="text" @input="onInputText">
<input type="number" :value="modelValue" @change="onChangeNumber">
</template>
子コンポーネントのスクリプト
<script lang="ts" setup>
const props = defineProps<{
text: string
modelValue: number
}>()
const emits = defineEmits<{
(e: 'update:text', text: string): void
(e: 'update:modelValue', num: number): void
}>()
const onInputText = (e: Event) => {
const target = e.target as HTMLInputElement
emits('update:text', target.value)
}
const onChangeNumber = (e: Event) => {
const target = e.target as HTMLInputElement
emits('update:modelValue', Number(target.value))
}
</script>
Vue2では、.syncを使って記述していました。代わりにv-modelの引数を利用します。
以上、Vue3でカスタムコンポーネントのv-modelの使用方法について、その使用方法と変更点を説明しました。これさえ知っておけば最低限のコードは記述できるはずです。
Vue.js公式ドキュメント
https://v3.ja.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A6%81
最新の記事
カテゴリー一覧
アーカイブ