5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

(vue3でのつまずき) v-modelが動かず、change は2回発火される場合の対処

Last updated at Posted at 2021-05-13

vue3 を触っていて、2つのつまずきがあったので解決方をまとめました。

■つまずき1:v-modelが動かない

components/AInput.vue

<template>
    <input
        @input="$emit('input', $event.target.value)"
        @change="$emit('change', $event.target.value)"
    />
</template>

<script>
export default {
  name: "AInput",
};
</script>

このコンポーネントにv-modelでバインドする際にvue3だとうまく動かないようでした。

App.vue
<template>
  <div class="container">
    コードを入力してください。
    <AInput 
      v-model="code"
      @change="onChange"
      type="tel"
    />
    <p>コード: {{ code }}</p>
  </div>
</template>

<script>
import AInput from "./components/AInput.vue";

export default {
  name: "App",
  components: {
    AInput,
  },
  data() {
    return {
      code: '',
    };
  },
  methods: {
    onChange(v) {
      console.log('changed')
      console.log(v)
    }
  }
};
</script>

ph1.png

■原因 v-modelの仕様が変わった

vue3 の v-modelの仕様が、v2から変わっていました。
https://v3.ja.vuejs.org/guide/migration/v-model.html

破壊的変更: カスタムコンポーネントで使用する場合に、v-model のプロパティとイベントのデフォルト名が変更されます。
プロパティ: value -> modelValue
イベント: input -> update:modelValue
破壊的変更: v-bind の .sync 修飾子とコンポーネントの model オプションは削除され、v-model の引数に置き換えられます。
新規: 同じコンポーネントに複数の v-model バインディングが可能になりました。
新規: カスタムの v-model 修飾子を作成する機能が追加されました。

こちらの記事に詳しく記載を頂いていました。

■解決法 $emit するイベントを update:modelValue に変更

記載の通り、AInput.vue の @input の $emit するイベントを update:modelValue に変更するとv-modelが動きました。

components/AInput.vue

<template>
    <input
        @input="$emit('update:modelValue', $event.target.value)"
        @change="$emit('change', $event.target.value)"
    />
</template>

<script>
export default {
  name: "AInput",
};
</script>

ph2.png

■つまずき2:changeイベントが2回発火される

また同時に、changeが2回$emitされる現象が起こりました。
ph3.png

■原因 vue3から $emit の仕様が変わった

下記のページとRFCを見ると、コーディングの意図を明示的にするために、$emitするイベントをwhite list化することを求める変更が行われたようでした。

■解決法 $emit するイベントをemitsに記述

こちらのissueの回答の通り、AInput.vue の $emit するイベントを 事前に把握して

export default {
  emits: ["update:modelValue","change"],
} 

と記述することでchangeが2回発火されなくなりました。

components/AInput.vue

<template>
    <input
        @input="$emit('update:modelValue', $event.target.value)"
        @change="$emit('change', $event.target.value)"
    />
</template>

<script>
export default {
  name: "AInput",
  emits: ["update:modelValue", "change"],
};
</script>

ph4.png

以上2つのつまずきについて解決方法をまとめました。
2つ目の方は、2回イベントが$emitされるのは結構危ないので気を付けたいと思いました。

以上です。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?