はじめに
Vue 2 のリリースから Vue 3 の開発に至るまで、双方向データバインディングのより柔軟な利用を実現するため、様々な取り組みが行われてきました。本稿では Vue 3 のリリースに伴い v-model
が 双方向データバインディングの標準化 に向けて、どのような破壊的変更がされたかについて解説します。
TL;DR
- コンポーネント利用時の
prop
とevent
のデフォルト名の変更 .sync
修飾子の廃止、代替構文の追加- 同一コンポーネントで複数の
v-model
の利用可 - カスタム修飾子の作成可
コンポーネント利用時の prop
と event
のデフォルト名の変更
v-model
はユーザーの入力イベントにおいてデータを更新するための糖衣構文です。Vue 2 のコンポーネントで v-model
を利用した場合、以下のような糖衣構文になります。詳しい内容は『【Vue.js 再入門】 v-model を正しく理解して親子間コンポーネントのデータ伝播をマスターする』を参照ください。
<MyComponent
:value="message"
@input="message = $event"
/>
Vue 3 のリリースに伴い、コンポーネント利用時の prop
と event
のデフォルト名が変更になりました。こちらは HTML 属性の名前の衝突を避けるために変更されました。
-
prop
:value → modelValue -
event
:input → update:modelValue
.sync
修飾子の廃止、双方向データバインディングの標準化
.sync
修飾子は v-model
と同様に props
の値に対して双方向データバインディングを実現できます。主な違いは .sync
修飾子を利用すれば、同一コンポーネントに複数の双方向データバインディングを実現できることです。
<MyComponent
:first-name.sync="firstName"
:last-name.sync="lastName"
/>
しかし .sync
修飾子の存在は双方向データバインディングを実現する上で 混乱を招く要因 になります。ゆえに Vue 3 では .sync
修飾子を廃止し、v-model
をより柔軟に利用できるよう、双方向データバインディングを標準化しました。そこで v-model
には新機能の「 v-model
の引数」と「同一コンポーネントに対する v-model
の複数可」が追加されました。
v-model
の引数
v-model
の引数は .sync
修飾子の廃止に伴い追加された v-model
の新しい構文です。下図のように v-model
に引数を渡せるようになり、.sync
修飾子と同等の機能を有します。ここで v-model
というのは v-model:model-value
の省略形であることが理解できます。ちなみに、本機能の追加によりコンポーネントの model
オプションが不要になり Vue 3 で削除されました。
同一コンポーネントに対する v-model
の複数可
Vue 3 では同一コンポーネントに複数の v-model
を利用可能になりました。.sync
修飾子に代わる v-model
の新機能です。
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
ちなみに上記のコード例の糖衣構文は以下のようになります。
<UserName
:first-name="firstName"
@update:firstName="firstName = $event"
:last-name="lastName"
@update:lastName="lastName = $event"
/>
カスタム修飾子の作成可
Vue 3 は v-model
に独自のカスタム修飾子を追加できるようになりました。実際に「先頭の文字を大文字にする」機能を有する .capitalize
カスタム修飾子の実装例で説明します。
<MyComponent v-model.capitalize="myText" />
独自のカスタム修飾子があるコンポーネントは props
経由で modelModifiers
にオブジェクト形式で含まれます。本例の modelModifiers
には { capitalize: true }
が格納されています。デフォルトの場合 modelModifiers
という名前になりますが、v-model
に引数を渡す場合は arg + Modifiers
という命名規則になります。
<template>
<input type="text" @input="handleInput" />
</template>
<script>
export default {
props: ['modelModifiers'],
emits: ['update:modelValue'],
setup(props, { emit }) {
const handleInput = (event) => {
let value = event.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
return {
handleInput,
}
},
}
</script>
setup
関数の第 1 引数のprops
を分割代入しないように注意しましょう。props
経由のデータのリアクティブが失われます。
さいごに
ここまで双方向データバインディングの標準化に向けた v-model
の新機能や、破壊的変更について取り上げました。本稿が読者の理解の一助になれば幸甚です。