【Vue 3】コンポーネント間のデータの受渡し【基礎】

# 1. はじめに　　
## 1-1. はじめに
Vueのコンポーネント間のデータの受渡しが「親→子」「子→親」で異なり複雑です。
私の頭を整理するために、この記事を記載しています。

## 1-2. 説明の前に・・・
説明で使用するプログラム ( x.html, x.js ) は、下記の.htmlファイルに記載する前提での説明です。
また、記載はVue3ですが、基本的にVue2でも同じ考え方です。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/vue@next"></script>
  <title>vue sample</title>
</head>
<body>
  <div id="app">
      // **************************************
      // ここにx.htmlを記載
      // **************************************
  </div>
  <script>
      // **************************************
      // ここにx.jsを記載
      // **************************************
  </script>
</body>
</html>
```
# 2. コンポーネントの基礎
## 2-1. コンポーネントとは
コンポーネントは***「名前付きの再利用可能なインスタンス」***です。
`2-1.js`のようにコンポーネントを定義し、`2-1.html`で定義したコンポーネントを3つ使用しています。
コンポーネントが再利用され、3つのボタンが表示されます。

```html:2-1.html
<div id="app">
  <Counter></Counter>
  <Counter></Counter>
  <Counter></Counter>
</div>
```

```js:2-1.js
// アプリケーションの生成
const app = Vue.createApp({})

// コンポーネントを定義 (第一引数：コンポーネントの名前, 第二引数：コンポーネントオブジェクト)
app.component('Counter',{
  data() {
    return {
      count : 0
    }
  },
  template: `
    <button v-on:click="count++"> コンポーネントだよ：{{count}} </button>
  `
})

// id="app" にマウント
app.mount('#app')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205371/19e65bcb-824b-b884-1297-fe5a7f9f0e02.png)

## 2.2 グローバルコンポーネントとローカルコンポーネント
コンポーネントには2種類あります。***グローバルコンポーネント***と***ローカルコンポーネント***です。

* ***グローバルコンポーネント*** 
アプリケーションの`component`メソッドを利用します。 
アプリケーション内の***どのコンポーネント***のテンプレートでも使用できます。 
2-1で定義したコンポーネントもグローバルコンポーネントです。 
実際はビルドの最適化を高めるために、ローカルコンポーネントで定義するほうが一般的です。　　　
   
* ***ローカルコンポーネント***
オブジェクトを定義して、`components`オプションで登録します。　
***`components`で登録したコンポーネント内でのみ使用***できます。
 
以下の例は、ローカルコンポーネント`Child`をグローバルコンポーネント`Parent`の`components`に登録しています。 
`Parent`の中でのみ`Child`が使用できるようになります。 
この際、`Parent`を***親コンポーネント***(以降、親)、`Child`を***子コンポーネント***（以降、子）といいます。[^1]   
   
```html:2-2.html
<div id="app">
  <Parent></Parent>
</div>
```

```js:2-2.js
// アプリケーションの生成
const app = Vue.createApp({})

// ローカルコンポーネント+子コンポーネント
// オブジェクトを定義
const Child = {
  template: `
    <h2> Child ! </h2>
  `
}

// グローバルコンポーネント+親コンポーネント
app.component('Parent',{
  // コンポーネントの登録をする
  components: {
    Child
  },
  // 親コンポーネントの中で子コンポーネントは使用できる
  template: `
    <h1> Parent ! </h1>
    <Child></Child>
  `
})

// id="app" にマウント
app.mount('#app')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205371/1dcd7460-ddb3-66f3-662a-9aba42975519.png)

# 3. コンポーネント間のデータの受渡しの基礎
## 3-1. 親コンポーネントから子コンポーネントへのデータの受渡し
子に`props`オプションを定義します。親からデータを渡すときはhtmlの属性を使用するように渡すことができます。
基本的にVueでは親から子への単方向のデータフローです。
`props`の定義の仕方を変えると、[バリデーションも付けられます。](https://v3.ja.vuejs.org/guide/component-props.html#%E5%8D%98%E6%96%B9%E5%90%91%E3%83%86%E3%82%99%E3%83%BC%E3%82%BF%E3%83%95%E3%83%AD%E3%83%BC)

```html:3-1.html
  <div id="app">
    <Parent></Parent>
  </div>
```

```js:3-1.js
// アプリケーションの生成
const app = Vue.createApp({})

// 子コンポーネント
const Child = {
  // 親から受け取るdataを定義
  props:['parentData'],
  template: `
    <h2> Child ! </h2>
    <h2> {{parentData}} </h2>
  `
}

// 親コンポーネント
app.component('Parent',{
  components: {
    Child
  },
  // htmlの属性のように記載してデータを渡す
  template: `
    <h1> Parent ! </h1>
    <Child parentData="親コンポーネントから書き換え！"></Child>
  `
})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205371/a35376cd-b605-d1e3-476a-5d7a921a51f3.png)

## 3-2. 子コンポーネントから親コンポーネントへのデータの受渡し
Vueは親から子への単方向データフローです。そのため、子から親へデータを受け渡すには少し工夫が必要です。

1. `$emit`を使用して、子から親のイベントを発火させます。
1. イベントで設定されたJavaScriptやメソッドを実行させ、その中で親のデータを変更します。   
   データの受取り方法は2種類あります。`$event`を使用する方法とメソッドの第一引数で受け取る方法です。

```html:3-2.html
  <div id="app">
    <Parent></Parent>
  </div>
```

```js:3-2.js
const app = Vue.createApp({})

// 子コンポーネント
const Child = {
  // $emitの第一引数はイベント名(childData)、第二引数は渡す値("親に渡すデータ")を定義
  template: `
    <button v-on:click='$emit("childData","親へ渡すデータ")'>親へデータ渡すボタン</button>
  `
}

// 親コンポーネント
app.component('Parent',{
  data() {
    return {
      eventData: '',  // $eventで渡したデータを格納
      methodData: '', // メソッドで渡したデータを格納
    }
  },
  components: {
    Child
  },
  // $eventを使用する方法とメソッドで渡す方法がある
  template: `
    <h1> Parent ! </h1>
    
    <h3>1. $eventを使用する方法</h3>
    <Child v-on:childData="eventData = $event"></Child>
    eventData : {{eventData}}

    <h3>2. メソッドで渡す方法</h3>
    <Child v-on:childData="changeParentData"></Child>
    methodData : {{methodData}}
  `,
  methods: {
    // methodの第一引数としてデータが渡される
    changeParentData(event) {
      this.methodData = event 
    }
  }
})

// id="app" にマウント
app.mount('#app')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205371/13e3ab88-1539-aa28-d7d3-94ed5a0c81d6.png)

# 4. おわりに
最後まで読んでいただきありがとうございました。今回の記事のポイントは以下です。

* Vueは親コンポーネントから子コンポーネントへの単一方向のデータフローである。
* 親→子：子で`props`を用いてデータを受け取る。
* 子→親：子にて`$emit`を使用してイベントを起動。親にて`$event`やメソッドでデータを受け取る。

記載の誤りや、文字の揺らぎがあるかもしれません。ご指摘がありましたらコメントいただけると幸いです。
詳細が知りたい方や、より正確な情報を知りたい方は参考URL（Vue3公式ページ）を参照してください。

## 参考URL
https://v3.ja.vuejs.org/guide/component-registration.html#%E5%91%BD%E5%90%8D%E3%81%AE%E3%82%B1%E3%83%BC%E3%82%B9-name-casing

https://v3.ja.vuejs.org/guide/component-props.html#%E3%83%95%E3%82%9A%E3%83%AD%E3%83%8F%E3%82%9A%E3%83%86%E3%82%A3%E3%81%AE%E5%9E%8B


[^1]: 親はグローバルコンポーネント、子はローカルコンポーネントというルールはありません。`components`オプションを定義しているのが親、定義に使用されているのが子です。