[nuxt].sync(propSync)を使って簡単に親子間コンポーネントで通信する

親から子コンポーネントに [props](https://jp.vuejs.org/v2/guide/components-props.html)を使って値を送信し、子から親へ [emit](https://jp.vuejs.org/v2/api/index.html#vm-emit)を使って変更を通知するような実装ってよくありますよね！

この時に受け渡す値の数だけ `props`と `emit`が増えて行くので、やりたいことの割にコードが煩雑になってどうにかならないかという悩みを [.sync演算子](https://jp.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A3%BE%E5%AD%90)を使って解決しましたのでその備忘録として残します。

また、 普段はnuxtに[vue-property-decorator](https://github.com/kaorun343/vue-property-decorator)を使って実装している場合についても記載します。


### 実行環境
vue: ^2.6.12

## propsとemitを使ったやり方

親からpropsを使って値を子に渡し、子は値の変更を [watch](https://jp.vuejs.org/v2/api/index.html#watch)して `emit`を使い親に値を渡しています。

```parent.vue
<template>
  <div class="container">
    <h1>親コンポーネント</h1>
    <child :name="name" :email="email" @name-change="setName" @email-change="setEmail" />
    <button @click="submit">送信</button>
  </div>
</template>

<script>
import Child from '../components/Child';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

export default {
  components: {
    Child
  },
  data() {
    return {
      name: initName,
      email: initEmail,
    }
  },
  methods: {
    submit() {
      alert(`name: ${this.name}, email: ${this.email}`)
    },
    setName(val) {
      this.name = val;
    },
    setEmail(val) {
      this.email = val;
    }
  }
}
</script>
```

```Child.vue
<template>
  <div class="child-container">
    <h2>子コンポーネント</h2>
    <div class="form">
      <label>
        名前
        <input type="text" v-model="nameValue" />
      </label>
      <label>
        メールアドレス
        <input type="text" v-model="emailValue" />
      </label>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: ''
    },
    email: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      nameValue: this.name,
      emailValue: this.email,
    }
  },
  watch: {
    nameValue: function(val) {
      this.$emit('name-change', val);
    },
    emailValue: function(val) {
      this.$emit('email-change', val);
    }
  }
}
</script>
```

ただ親子で値を共有したいだけなのに、 `親data()`、 `props` 、 `子data()`とバケツリレーを行うことでやっと達成できました。
やっていることは簡単なのにコードが煩雑です。
このやり方でフォームの値がどんどん増えて行くと、大変なことになります。。。

## .sync演算子を使った場合

.sync演算子を使うことによって、`parent.vue`で$emitで受け取った値を更新する methodが必要なくなります。
また、 `Child.vue`で `input`イベントで `emit`を使い通信を行うようにしました。

```parent.vue
<template>
  <div class="container">
    <h1>親コンポーネント</h1>
    <child :name.sync="name" :email.sync="email" />
    <button @click="submit">送信</button>
  </div>
</template>

<script>
import Child from '../../components/sync/Child';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

export default {
  components: {
    Child
  },
  data() {
    return {
      name: initName,
      email: initEmail,
    }
  },
  methods: {
    submit() {
      alert(`name: ${this.name}, email: ${this.email}`)
    },
  }
}
</script>
```

```Child.vue
<template>
  <div class="child-container">
    <h2>子コンポーネント</h2>
    <div class="form">
      <label>
        名前
        <input type="text" :value="name" @input="$emit('update:name', $event.target.value)" />
      </label>
      <label>
        メールアドレス
        <input type="text" :value="email" @input="$emit('update:email', $event.target.value)" />
      </label>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: ''
    },
    email: {
      type: String,
      default: ''
    }
  },
}
</script>
```

スッキリしましたね！
同じ値が入っているpropertyや同じ動作を行うmethodを減らすことができました。


## vue-property-decoratorでpropSyncを使った場合

vue-property-decoratorでも `.sync`演算子が使えますので備忘録として残します。

### 実行環境
nuxt: ^2.14.0
vue-property-decorater: ^9.0.0

```parent.vue
<template>
  <div>
    <h1>親コンポーネント</h1>
    <child :nameValue.sync="name" :emailValue.sync="email" />
    <button @click="submit">
      送信
    </button>
  </div>
</template>

<script lang="ts">
import {Vue, Component} from 'vue-property-decorator';
import Child from '../components/child.vue';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

@Component({
  components: {
    Child
  }
})
export default class Parent extends Vue {
  name: string = initName;
  email: string = initEmail;

  submit(): void {
    alert(`name: ${this.name}, email: ${this.email}`);
  }
}
</script>
```

```Child.vue
<template>
  <div>
    <h2>子コンポーネント</h2>
    <div>
      <label>
        名前
        <input type="text" v-model="name" />
      </label>
      <label>
        メールアドレス
        <input type="text" v-model="email" />
      </label>
    </div>
  </div>
</template>

<script lang="ts">
import {Vue, Component, PropSync} from 'vue-property-decorator';

@Component
export default class Child extends Vue {
  @PropSync('nameValue', { type: String }) name!: string
  @PropSync('emailValue', { type: String }) email!: string
}
</script>
```

`Child.vue`に`emit`を書くこともなくなるのでスッキリしますね！