199
158

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.

【Vue】知っておきたい .sync修飾子のすゝめ

Last updated at Posted at 2019-06-27

Vue.js 2.3.0から実装された 「.sync 修飾子」がVue.jsを書いていると陥るよくある困りごとを解決したので紹介します!

サンプルコードはこちら↓
https://github.com/harhogefoo/vue-sync-modifier-sample

対象読者

  • Vue.js 経験者
  • 子コンポーネントに複数のプロパティをバインドさせたい人(Object型も含む)
  • これから大量の入力フォームを子コンポーネントに分割して実装していくんだぜ...な人

よくある困りごと

例えば、新規作成(new)、 編集(edit)それぞれのページの共通のフォームのコンポーネントを実装する場合、
そのコンポーネントにフォームの情報を持つObject型のプロパティを渡したくなりますよね。

Vueでは、プロパティを渡した先でプロパティの情報を更新しようとすると、
以下のようなwarningログが出力されます(ただしObject型やArray型を渡した場合は出力されません)。

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "propsValue"

親コンポーネントが再レンダリングされるたびに値が上書きされるため、propを直接変更しないでください。
代わりに、dataまたはcomputedプロパティを使用して値を変更して下さい。変化するprop: "propsValue"

Object型のプロパティを子コンポーネントに渡し、子コンポーネントで値を変更するリスク

Object型のプロパティを子コンポーネントに渡し、子コンポーネントで値を変更した場合、warningログは出力されません。
そのため、渡した値がどのコンポーネントで変更されているか把握し辛くなってしまいます。

したがって、
**「親の知らぬところで子コンポーネントが値を変更する方法」**はなるべく避け、
「子が親に通知して親が値を変更する方法」 を採用すると良いでしょう。

今回は「子が親に通知して親が値を変更する」を実現する手段として、.sync 修飾子を利用します。
また、.sync 修飾子はObject型などを直接バインドすることもできます。

この記事では

  • Object型の値ををコンポーネントに渡し、その値をコンポーネント内で更新しwarningを回避する方法
  • .sync修飾子を利用する方法

を比較しながら、.sync 修飾子の使い所を説明していきたいと思います。

🤔 Object型のプロパティを渡してwarningを回避する方法

親の知らぬところで子が値を更新してしまうパターン を以下に記載します。
確かにこの方法で複数のプロパティを更新するコンポーネントになりますが、
Vueのデータフローの思想に反してしまいます。

views/Parent.vue
<template>
  <div>
    <user-input-form :user="user" @submit="createUser" />
  </div>
</template>

<script>
import { defaultUser } from "@/helper/user";
import UserInputForm from "@/components/UserInputForm.vue";
export default {
  components: {
    UserInputForm
  },
  data() {
    return {
      user: defaultUser() // { email: '', name: '', ... }
    };
  },
  methods: {
    createUser() {
      console.log(this.user);
    }
  }
};
</script>
UserInputForm.vue
<template>
  <form @submit.prevent="$emit('send')">
    <label>
      メールアドレス
      <input type="text" v-model="user.email" />
    </label>
    <label>
      名前
      <input type="text" v-model="user.name" />
    </label>
    <button type="submit" @click="$emit('submit')">登録</button>
  </form>
</template>

<script>
export default {
  props: {
    user: {
      type: Object,
      default: () => ({})
    }
  }
};
</script>

👍 .sync修飾子を使って値の変更を通知する方法

では、子は親から渡ってきたデータが更新されたことを子から親に通知するために、.sync修飾子を使って実現していきましょう。

:email.sync="user.email" が今回のポイントです。

views/Parent.vue
<template>
  <div>
    <user-input-form
      :email.sync="user.email"
      :name.sync="user.name"
      @submit="createUser"
    />
  </div>
</template>

<script>
import { defaultUser } from "@/helper/user";
import UserInputForm from "@/components/UserInputForm.vue";
export default {
  components: {
    UserInputForm
  },
  data() {
    return {
      user: defaultUser() // { email: '', name: '', ... }
    };
  },
  methods: {
    createUser() {
      console.log(this.user);
    }
  }
};
</script>

.sync 修飾子を付与すると、子コンポーネントからthis.$emit('update:email', $event.target.value) で親に通知することができます。(親は update:prop名のイベントを監視するようになります。)
参照: https://jp.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A3%BE%E5%AD%90

UserInputForm.vue
<template>
  <form @submit.prevent="$emit('send')">
    <label>
      メールアドレス
      <input
        type="text"
        :value="email"
        @input="$emit('update:email', $event.target.value)"
      />
    </label>
    <label>
      名前
      <input
        type="text"
        :value="name"
        @input="$emit('update:name', $event.target.value)"
      />
    </label>
    <button type="submit" @click="$emit('submit')">登録</button>
  </form>
</template>

<script>
export default {
  props: {
    email: {
      type: String,
      default: ""
    },
    name: {
      type: String,
      default: ""
    }
  }
};
</script>

👍 (より短いコードにする) .sync 修飾子にオブジェクトを渡す

親から子にオブジェクトを渡したい場合、.sync 修飾子にオブジェクトを渡すと、子コンポーネントのpropsで値が展開されます。

views/Parent.vue
<template>
  <div>
    <user-input-form v-bind.sync="user" @submit="createUser" />
  </div>
</template>

まとめ

子コンポーネントにはフォームに必要な全量のpropsを記載する必要がありますが、安全に値を渡し、子から親に通知するために必要な作業と考えると良いでしょう。

.sync 修飾子、いかがでしたでしょうか?

今回のサンプルコードは、以下にホストしていますのでご活用ください。
https://github.com/harhogefoo/vue-sync-modifier-sample

Happy Coding! @shino_tp

199
158
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
199
158

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?