【Vue 3 入門】双方向データバインディングの標準化


はじめに

Vue 2 のリリースから Vue 3 の開発に至るまで、双方向データバインディングのより柔軟な利用を実現するため、様々な取り組みが行われてきました。本稿では Vue 3 のリリースに伴い v-model双方向データバインディングの標準化 に向けて、どのような破壊的変更がされたかについて解説します。

‎TL;DR

  • コンポーネント利用時の propevent のデフォルト名の変更
  • .sync 修飾子の廃止、代替構文の追加
  • 同一コンポーネントで複数の v-model の利用可
  • カスタム修飾子の作成可

コンポーネント利用時の propevent のデフォルト名の変更

v-model はユーザーの入力イベントにおいてデータを更新するための糖衣構文です。Vue 2 のコンポーネントで v-model を利用した場合、以下のような糖衣構文になります。詳しい内容は『【Vue.js 再入門】 v-model を正しく理解して親子間コンポーネントのデータ伝播をマスターする』を参照ください。

<MyComponent
  :value="message"
  @input="message = $event"
/>

Vue 3 のリリースに伴い、コンポーネント利用時の propevent のデフォルト名が変更になりました。こちらは 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 の新機能や、破壊的変更について取り上げました。本稿が読者の理解の一助になれば幸甚です。

参考文献