Vue3の双方向バインディングv-modelを使ってみる

3 min read読了の目安(約3300字

abstract

Vue2では、親子間の双方向バインディングは.syncが利用されている。
Vue3からは.syncが廃止されてv-model:hogeに変わる。
その使い方

.syncとは

こんなの

<text-document v-bind:title.sync="doc.title"></text-document>

.syncをさらにほぐすとこんな感じになる。

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

.syncの使い方はこちら

https://jp.vuejs.org/v2/guide/components-custom-events.html#sync-修飾子

v-modelに変えてみる

Vue3の双方向バインディング

<text-document v-model:title="doc.title"></text-document>

簡単に置き換えられる。

親子間で監視する

作るもの

親と子のコンポーネントでそれぞれinputを用意してバインディングさせてみる。

親コンポーネント

Parent.vue
<template>
  <div>
    <p>parent</p>
    <input type="text" v-model="titleComputed" />
    <ChildInput v-model:title="titleComputed" />
  </div>
</template>

<script>
import { computed, defineComponent, ref } from 'vue'
import ChildInput from './Child.vue'

export default defineComponent({
  name: 'ParentInput',
  components: {
    ChildInput,
  },
  setup() {
    const title = ref('')
    const titleComputed = computed({
      get: () => title.value,
      set: (value) => (title.value = value),
    })

    return {
      titleComputed,
    }
  },
})
</script>

子コンポーネント

<template>
  <div>
    <p>child</p>
    <input type="text" v-model="titleComputed" />
  </div>
</template>

<script>
import { defineComponent, toRefs, computed } from 'vue'
export default defineComponent({
  name: 'ChildInput',
  components: {
    GrandChild,
  },
  props: {
    title: String,
  },
  emits: ['update:title'],
  setup(props, { emit }) {
    const { title } = toRefs(props)
    const titleComputed = computed({
      get: () => title.value,
      set: (value) => {
        emit('update:title', value)
      },
    })

    return {
      titleComputed,
    }
  },
})
</script>

わー

孫も追加してみる

蛇足で三世代バインディングを作ってみる。

子コンポーネントを編集

Child.vue
<template>
  <div>
    <p>child</p>
    <input type="text" v-model="titleComputed" />
+    <GrandChild v-model:title="titleComputed" />
  </div>
</template>

<script>
import { defineComponent, toRefs, computed } from 'vue'
+import GrandChild from './GrandChild.vue'
export default defineComponent({
  name: 'ChildInput',
  components: {
    GrandChild,
  },
  props: {
    title: String,
  },
  emits: ['update:title'],
  setup(props, { emit }) {
    const { title } = toRefs(props)
    const titleComputed = computed({
      get: () => title.value,
      set: (value) => {
        emit('update:title', value)
      },
    })

    return {
      titleComputed,
    }
  },
})
</script>

孫コンポーネント追加

<template>
  <div>
    <p>grand child</p>
    <input type="text" v-model="titleComputed" />
  </div>
</template>

<script>
import { computed, defineComponent, toRefs } from 'vue'

export default defineComponent({
  props: { title: null },
  emits: ['update:title'],
  setup(props, { emit }) {
    const { title } = toRefs(props)
    const titleComputed = computed({
      get: () => title.value,
      set: (value) => emit('update:title', value),
    })

    return {
      titleComputed,
    }
  },
})
</script>

わあー

参考:https://qiita.com/ponko2bunbun/items/d05d90bc10be61c0e3af#comments