どうもこんにちは!樋渡です。
今回は、Vue3.4で安定版になった「defineModel」についてのお話です。
実験的に導入されたVue3.3の時から非常に便利だと思っていたので、これで気兼ねなく使うことができます。
defineModelは主に、親子間での双方向バインディングをしたい時に使います。
以前までの書き方よりも、直観的かつ簡略化して記述することが出来ます。
早速、具体的な使用方法について触れていきましょう。
今回はインプットフォームのコンポーネントを例に説明していきます。
まずは、共通して使う親側のvueファイルを記述します。
▼親コンポーネント
// parent.vue
<script lang="ts" setup>
import children from "./children.vue";
import { ref } from "vue";
const message = ref("")
</script>
<template>
<children type="text" v-model="message" />
</template>
そして、従来の書き方で親子間の双方向バインディングをすると下記のようになります。
▼子コンポーネント(以前までの記述)
// children.vue
<script lang="ts" setup>
const props = defineProps({
modelValue: {
type: String,
required: true,
})
const emit = defineEmits(['update:modelValue'])
const model = computed({
get: () => props.modelValue,
set: (val: string) => emit('update:modelValue', val)
})
</script>
<template>
<input type="text" v-model="model" />
</template>
今見ても少しややこしい書き方だなと思います。
インプットフォームのコンポーネントくらいであれば、もっとシンプルに書きたいものです。
defineModelを使えば、もっと短い記述で全く同じことができます。
▼子コンポーネント(defineModelを使った記述)
// children.vue
<script lang="ts" setup>
const model = defineModel<string>()
</script>
<template>
<input type="text" v-model="model" />
</template>
以前までの記述と比べると、非常にシンプルですよね。
複数のv-modelを渡したい時は下記のようにします。
▼子コンポーネント(複数のv-modelを渡す)
// children.vue
<script lang="ts" setup>
const name = defineModel<string>("name")
const address = defineModel<string>("address")
</script>
<template>
<input type="text" v-model="name" />
<input type="text" v-model="address" />
</template>
▼親コンポーネント
// parent.vue
<script lang="ts" setup>
import children from "./children.vue";
import { ref } from "vue";
const name = ref("")
const address = ref("")
</script>
<template>
<children type="text" v-model:name="name" />
<children type="text" v-model:address="address" />
</template>
子コンポーネント側でそれぞれに名前を付けてあげて、親側で指名して使うといった感じです。
また、defaultやrequiredのオプションも付与することが出来ます。
▼子コンポーネント(default等のオプションを付ける)
// children.vue
<script lang="ts" setup>
const name = defineModel<string>("name", { required: false, default: "匿名" })
const address= defineModel<string>("address", { required: true })
</script>
<template>
<input type="text" v-model="name" />
<input type="text" v-model="address" />
</template>
さらに、親から渡ってきた値を少し変更したい場合は、computedを使わなくもdefineModelに直接、getやsetが使えるようです。
できる事が同じで短く書けるなら、それに越したことはないですよね。
親子間の双方向バインディングは使用頻度が高いと思いますので、まだ使ったことがない方はぜひ試してみてください。