Vue 3.4 defineModelによる双方向バインディング
Vue 3.4バージョン以前では、カスタムコンポーネントで双方向バインディングを実現するためには、modelValueプロパティとupdate:modelValueイベントを手動で定義し、さらにcomputedを使用してバインディングを実装する必要がありました。この方法は機能的には問題ありませんが、コードが冗長で直感的ではありません。
以下は、Vue 3.4以前の双方向バインディングの従来の方法です
<!-- CustomInput.vue -->
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
modelValue: string;
}>();
const emits = defineEmits<{
'update:modelValue': [value: string];
}>();
const value = computed({
get: () => props.modelValue,
set: (value: string) => emits('update:modelValue', value),
});
</script>
<template>
<input v-model="value" />
</template>
親コンポーネントでこのコンポーネントを使用する場合:
<!-- App.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import CustomInput from './components/CustomInput.vue';
const inputValue = ref<string>('');
</script>
<template>
<CustomInput v-model="inputValue" />
</template>
この実装方法では、definePropsでmodelValueを定義し、defineEmitsでupdate:modelValueを定義し、さらにcomputedを使ってデータバインディングを処理する必要があります。機能は十分ですが、手順が複雑です。
Vue 3.4で導入された新機能:defineModel
Vue 3.4では、新たにdefineModelという関数が追加され、双方向バインディングの実装が大幅に簡略化されました。以下はdefineModelを使用した方法です:
<!-- CustomInput.vue -->
<script setup lang="ts">
import { defineModel } from 'vue';
const val = defineModel<string>();
</script>
<template>
<input v-model="val" />
</template>
defineModelを使用することで、modelValueやupdate:modelValueを明示的に宣言する必要がなくなり、直接v-modelの双方向バインディングがサポートされます。これにより、コードが簡潔になり、可読性が向上します。
他の使い方
1. デフォルト値の設定
defineModelは、モデルバインディングにデフォルト値を直接指定することをサポートしています:
const val = defineModel({ type: String, default: 'デフォルト値' });
親コンポーネントが値を渡さなかった場合、子コンポーネントはデフォルト値を使用します。
2. 複数モデルのバインディング
defineModelは、複数のモデルを定義して複雑なシナリオを処理することも可能です:
<!-- CustomInput.vue -->
<script setup lang="ts">
import { defineModel } from 'vue';
const name = defineModel<string>('name');
const address = defineModel<string>('address');
</script>
<template>
<input v-model="name" placeholder="Name" />
<input v-model="address" placeholder="Address" />
</template>
親コンポーネントでの使用例:
<!-- App.vue -->
<script setup lang="ts">
import { ref } from 'vue';
import CustomInput from './components/CustomInput.vue';
const nameInput = ref<string>('John Doe');
const addressInput = ref<string>('123 Main St');
</script>
<template>
<CustomInput v-model:name="nameInput" v-model:address="addressInput" />
</template>
この方法により、複数のデータモデルを簡単にバインドでき、コードがより柔軟になります。
まとめ
defineModelは、Vue 3.4の重要なアップデートであり、カスタムコンポーネントの双方向バインディングをより簡単かつ直感的にしました。defineModelを使用することで、テンプレートコードの複雑さを軽減し、複数モデルのバインディングやデフォルト値の設定も簡単に実現できます。もしプロジェクトをVue 3.4以上のバージョンにアップグレード可能であれば、従来のmodelValueの実装方法の代わりにdefineModelを使用することを強くお勧めします。