## 事象 以下のような`CountUpButton`コンポーネントを作成しても、[[v-model]]が期待通り動かない。このコードはもともと[[Vue2]]で動作していたものをそのまま使おうとしている。 `main.vue` ```html <script setup lang="ts"> import { ref } from "vue"; import CountUpButton from "./components/CountUpButton.vue"; const count = ref(0); </script> <template> <CountUpButton v-model="count" /> </template> ``` `CountUpButton.vue` ```html <script setup lang="ts"> const props = defineProps<{ value: number; }>(); const emit = defineEmits<{ input: [value: number]; }>(); const handleClick = () => { emit("input", props.value + 1); }; </script> <template> <button @click="handleClick">Count up {{ value }}</button> </template> ``` 以下のように途中が省略された長い型エラーが出る。 ```error Argument of type '{ modelValue: number; }' is not assignable to parameter of type 'Partial<{}> & Omit<{ readonly value: number; onUpdate?: ((value: number) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Readonly<...> & { ...; }, never> & Record<...>'. Property 'value' is missing in type '{ modelValue: number; }' but required in type 'Omit<{ readonly value: number; onUpdate?: ((value: number) => any) | undefined; } & VNodeProps & AllowedComponentProps & ComponentCustomProps & Readonly<...> & { ...; }, never>'. ``` ## 原因 [[Vue3のv-modelに対する破壊的に変更]]に対応していないから。 [[Vue3ではv-model相当のpropにmodelValue(model-value)を使う]]ので、`value`のpropは認識されない。また、`input`も`update:???`でなくてはいけない。 ## 解決方法 インターフェースを変える場合と変えない場合で2通りの方法がある。 ### インターフェースを変える方法 `v-model`を`v-model:value`に変えることで、コンポーネントのpropは変更しなくて済む。 ```html <template> <CountUpButton v-model:value="count" /> </template> ``` ただし、eventは変更が必要。`input`は`update:value`にしなければならない。 `CountUpButton.vue` ```html <script setup lang="ts"> const props = defineProps<{ value: number; }>(); const emit = defineEmits<{ "update:value": [value: number]; }>(); const handleClick = () => { emit("update:value", props.value + 1); }; </script> <template> <button @click="handleClick">Count up {{ value }}</button> </template> ``` いずれにせよ変更が必要なら、このあとの[[#インターフェースを変えない方法]]の方がいい気がする。 ### インターフェースを変えない方法 propsの`value`を`modelValue`に変更し、`inpute`を`update:modelValue`に変更する。ライブラリとしてコンポーネントを提供する場合はこの方が親切である。 `CountUpButton.vue` ```html <script setup lang="ts"> const props = defineProps<{ modelValue: number; }>(); const emit = defineEmits<{ "update:modelValue": [value: number]; }>(); const handleClick = () => { emit("update:modelValue", props.modelValue + 1); }; </script> <template> <button @click="handleClick">Count up {{ modelValue }}</button> </template> ``` ```html <template> <CountUpButton v-model="count" /> </template> ```