vue.jsを初めて学ぶ ⑤ [ コンポーネントの扱いについて ]

前回までの記事

[vue.jsを初めて学ぶ ① [hello world]](https://qiita.com/uechikohei/items/4e44380848d29bf3fd6a)

[vue.jsを初めて学ぶ ② [テンプレート構文]](https://qiita.com/uechikohei/items/dd857da50a3f0237c22b)

[vue.jsを初めて学ぶ ③ [レンダリング] ](https://qiita.com/uechikohei/items/6433c89813cde1c7f1f1)

[vue.jsを初めて学ぶ ④ [VueCLI 3.7.0 + Vue.js 2系のインストール] ](https://qiita.com/uechikohei/items/5f89c785177b7b5581f5)


目次
---

[1. Vue.jsの、コンポーネントとは?](#コンポーネントとは)

[2. ローカル登録とグローバル登録について](#ローカル登録-と-グローバル登録)

[3. Vueプロジェクトにおける、コンポーネントの概念](#コンポーネントの概念)

[4. templateタグの注意点とは？](#templateタグの注意点)

[5. ケバブケースとパスカルケースの記法について](#ケバブとパスカルケース記法)

[6. (親と子)コンポーネント間のデータ受け渡しについて](#コンポーネント間のデータ受け渡し)

[](#)
[](#)
[](#)

---

# コンポーネントとは

**以前までの記事とは、書き方が異なってくる**


## <font color="green">**いいね数を表示する**</font>

#### <font color="blue">従来のVue.js書き方</font>

```text:index.html

<div id="app">
</div>
```

```text:index.js

new Vue({
  el:'#app',
  data: {
    number: 12
  },
  template: '<p>いいね{{number}}</p>'
})
```

## <font color="red">コンポーネントを利用した書き方</font>

```text:index.html

<div id="app">
  <my-component></my-component>
</div>
```

```text:index.js

Vue.component('my-component', {
  data: function(){
    return {
      number: 12
    }
  },
  template: '<p>いいね{{number}}</p>'
})

new Vue({
  el:'#app',
})
```

1. **Vue.component**を作成する。

2. **Vue.component**では、<br><font color="red">**第1引数=>[コンポーネント名]**</font><br><font color="green">**第2引数=>[プロパティ]**</font><br>が入る

3. **Vue.component**内の<font color="blue">**dataプロパティ**</font>では、<br><font color="green">**関数(returnによる返り値)を用意しないと駄目**</font>

4. **new Vueインスタンス**内の<font color="blue">**elプロパティ**</font>では、<br>html側の'#app'を指定している

5. html側では、<br><font color="red">**コンポーネント名の第1引数をタグとして用意**</font>、<br>代入される。


## なぜ？ <br>コンポーネントでは、<br><font color="blue">dataプロパティ</font>内の変数を<br>関数として扱わないと駄目か？


1. **<font color="green">オブジェクト形式</font>では、<br>変数が参照→変数に処理が加わる<br><font color="red">全コンポーネントで適用</font>**

2. **<font color="green">関数の形式</font>では、<br>変数に処理が加わる<br><font color="red">単体のコンポーネントのみに適用</font>**

### 実例 : +1されるボタン(オブジェクトver)

```text:index.html
  <div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
  </div>
```

```text:index.js

//無理やり、オブジェクト形式のdataをreturnにセットしている。
    var data = {
      number: 12
    }

    Vue.component('my-component', {
      data: function(){

//returnされているけど、中身はオブジェクトのnumberが入っている。
          return data
      },
      template: '<p>いいね{{number}}<button v-on:click="countUp">+1</button></p>',
      methods: {
        countUp: function(){
          this.number += 1;
        }
      }
    }),
    new Vue({
      el:'#app',
    })
```

**オブジェクト形式のnumber(12)を+1すると、**

![スクリーンショット 2020-10-29 19.40.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/cd3d68ad-b514-7047-3357-9c3214e32137.png)


<fotn color="red">**すべてのコンポーネント内の変数numberに適用**</font>

## 実例 : +1されるボタン(関数ver)

```text:index.html
  <div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
  </div>
```

```text:index.js

    Vue.component('my-component', {
      data: function(){
        return {
          number: 12
        }
      },
      template: '<p>いいね{{number}}<button v-on:click="countUp">+1</button></p>',
      methods: {
        countUp: function(){
          this.number += 1;
        }
      }
    }),
    new Vue({
      el:'#app',
    })
```

**単体のコンポーネントに対して適用されているのが分かる。**

![スクリーンショット 2020-10-29 19.42.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/93181350-f4df-782c-aa6d-eec51201cf98.png)

---


# ローカル登録 と グローバル登録

## グローバル登録とは?

- **<font color="red">全インスタンスが使用できる</font>**

```text:index.html

  <div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
    <hr>
  </div>

  <div id="app2">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
    <hr>
  </div>
```

```text:index.js

    Vue.component('my-component', {
      data: function(){
        return {
          number: 12
        }
      },
      template: '<p>いいね{{number}}<button v-on:click="countUp">+1</button></p>',
      methods: {
        countUp: function(){
          this.number += 1;
        }
      }
    }),
    new Vue({
      el:'#app',
    })
    new Vue({
      el:'#app2',
    })
```
**グローバル登録の結果**

![スクリーンショット 2020-10-29 20.14.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/1d945950-a8ae-b0bd-5c7a-d26b12c4e8d0.png)

**インスタンス<font color="red">'div#app'</font> と <font color="red">'div#app2'</font> どちらも適用**

---

## ローカル登録とは?

- **<font color="red">特定のインスタンスのみが使用できる</font>**

```text:index.html

  <div id="app">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
    <hr>
  </div>

  <div id="app2">
    <my-component></my-component>
    <my-component></my-component>
    <my-component></my-component>
    <hr>
  </div>
```

```text:index.js

    var component = {
      data: function(){
        return {
          number: 12
        }
      },
      template: '<p>いいね{{number}}<button v-on:click="countUp">+1</button></p>',
      methods: {
        countUp: function(){
          this.number += 1;
        }
      }
    }

    new Vue({
      el:'#app',
      components: {
        'my-component': component
      }
    })
    new Vue({
      el:'#app2',
    })
```

1. **varで、<br>変数を用意する。**

2. **変数に、<br>グローバル登録していた<br>コンポーネントの <font color="green">第2引数</font> をすべて格納する。**

3. **ローカル登録したいインスタンスへ、<br><font color="blue">componentsプロパティ</font> を持たせる。**

4. **<font color="blue">componentsプロパティ</font> は、<br><font color="red">オブジェクト形式</font>で<br><font color="green">・キーにhtml側で代入するタグ名</font><br><font color="green">・バリューには先程格納した変数名</font><br>を指定する**


**ローカル登録の結果**

![スクリーンショット 2020-10-29 20.13.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/9404fdf5-6261-8402-c49b-44998ee5fd13.png)

**インスタンス <font color="red">'div#app'</font> のみに、divタグが適用**


---


# コンポーネントの概念

- **Vueプロジェクトでは、<br><font color="green">main.js内のimport</font>によって、<br><font color="red">・指定の.vueファイル</font><br>を読み込んでコンポーネントとして使用している。**

- **<font color="green">App.vueファイル</font>では、<br><font color="green">import</font>によって、<br><font color="red">・componentsディレクトリ配下の.vueファイル</font><br>を読み込んでコンポーネントとして使用している。**

**[Vue-CLI3.7.0によって、vue createされたディレクトリ構成]**
![スクリーンショット 2020-10-29 22.20.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/26c9afda-5b0c-ce7f-f8cd-3e8de55a410e.png)

```text:main.js
// (1) 
import App from './App.vue'

// (2)
new Vue({
  render: h => h(App),
}).$mount('#app')
```

```text:App.vue

<script>
// (3) 
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
// (4)
    HelloWorld
  }
}
</script>

```

1. **<font color="green">ファイルのimport時</font>に、<br><font color="green">コンポーネント名がfromで指定</font>される。<br>各インスタンスで、使用できる**

---

# templateタグの注意点

**親要素として一つでないと、エラーとなる。**

### +1をするメソッド追加 <font color="red">エラー</font> パターン

```text:main.js

import Vue from 'vue'
import App from './App.vue'

// グローバル登録(1)
import LikeNumber from "./LikeNumber.vue"

Vue.config.productionTip = false

// グローバル登録(1)
Vue.component('LikeNumber', LikeNumber);

new Vue({
  render: h => h(App),
}).$mount('#app')

```

```text:App.vue

<template>
  <LikeNumber></LikeNumber>
</template>
```

```text:LikeNumber.vue

<template>
// 親要素が２つだと、エラーになる！
    <p>いいね({{number}})</p>
    <button @click="countUp">+1</button>
</template>

<script>
export default {
  data() {
    return {
      number: 6
    }
  },
  methods: {
    countUp(){
      this.number += 1;
    }
  }
}
</script>
```

### <font color="green">divタグで括り、親要素を１つに</font>


```text:LikeNumber.vue

<template>
  <div>
    <p>いいね({{number}})</p>
    <button @click="countUp">+1</button>
  </div>
</template>
```

**これでエラーがなくなる**

---

# ケバブとパスカルケース記法

```text:App.vue(パスカルケース)
<LikeHeader></LikeHeader>
```

```text:App.vue(ケバブケース)
<like-header></like-header>
```
**プロジェクト内では、どちらかのケース記法で統一しよう。**

<font color="green">**・HTML要素は、ケバブケース</font><br>なので目視でわかりやすくするため<br><font color="green">パスカルケースがおすすめ</font>**


## htmlタグ内では、ケバブケース記法統一がおすすめ!

```text:App.vue
// キャメルケース
    <LikeNumber :totalNumber="number"></LikeNumber>

// ケバブケース
    <LikeNumber :total-number="number"></LikeNumber>
```
**<font color="red">htmlでは、大文字を判別できない。<br></font>公式でもhtmlタグ内では、ケバブケースを推奨**

---

# コンポーネント間のデータ受け渡し

## 親要素から子要素への、データ受け渡し

### 各いいね数とトータルいいね数

```text:App.vue

<template>
  <div>
    <likeHeader></likeHeader>
    <h2>{{ number }}</h2>
    <LikeNumber :number="number"></LikeNumber>
    <LikeNumber :number="number"></LikeNumber>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10
    }
  },
  components: {
    LikeHeader
  }
}
</script>
```

**<font color="red">親要素であるApp.vue内</font>の<br><font color="green">dataプロパティ</font><br>number:10 を受け渡す**

### propsプロパティを使用する

```text:LikeNumber.vue

<template>
  <div>
    <p>いいね({{ number / 2 }})</p>
  </div>
</template>

<script>
export default {
  props: ["number"],
}
</script>
```

**<font color="red">子要素であるLikeNumber.vue内</font>にて、<br><font color="green">propsプロパティ</font>を 配列[ ] で指定する。<br>number:10が使用できる。**

![スクリーンショット 2020-10-30 10.43.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/0f1f44e0-3fc9-0852-53a5-04bb2e4a86e2.png)

---

## 子要素から親要素への、データ受け渡し

### +1ボタンを子要素に設置、押すと親要素のトータルいいね数が変更される。

- **<font color="red">$emit が必須！</font>**
  - カスタムイベントを作成できる。

```text:App.vue

<template>
  <div>
    <likeHeader></likeHeader>
    <h2>{{ number }}</h2>
    <LikeNumber :total-number="number" @my-click="number = $event"></LikeNumber>
    <LikeNumber :total-number="number"></LikeNumber>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10
    }
  },
  components: {
    LikeHeader
  }
}
</script>
```

```text:LikeNumber.vue

<template>
  <div>
    <p>いいね({{ harfNumber }})</p>
    <button @click="incriment">+1</button>
  </div>
</template>

<script>
export default {
  props: ["totalNumber"],
  computed: {
    harfNumber() {
      return this.totalNumber / 2;
    }
  },
  methods: {
    incriment() {
      this.$emit("my-click", this.totalNumber +1);
    }
  }
};
</script>
```

1. **<font color="green">LikeNumber.vueファイル内</font>で、<br><font color="blue">methodsプロパティ</font>で、<br>totalNumberに+1する関数を作成。<br>
<font color="red">$emit</font>を用いて、<br><font color="green">・第１引数にカスタムイベント名</font>を<font color="green"><br>・第２引数にイベント処理</font><br>を書く**


2. **<font color="green">App.vueファイル内</font>で、<br><font color="red">v-on:my-click時</font>、<br>親要素のnumberが<font color="red">$emit</font>で発火した値を$eventで拾い、値を更新する。**


![スクリーンショット 2020-10-30 11.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449720/bdfa9c67-89c2-a099-0d27-26f2fb8757f2.png)

---