Vue.js　コンポーネント

#Vue.js　コンポーネント

前回の記事はこちら
[Vue.js　フォーム入力バインディング
](https://qiita.com/fumiya0414/items/a5e1ef929095d6f011fc)

##コンポーネントとは
ページを構成するUI部品で再利用可能なインスタンスです。

HTMLベースのテンプレートとjavascriptで書かれたロジックで構成されます。

コンポーネント使うメリットは以下です。

①再利用ができる

②メンテナンス性が高まる

##グローバル登録

Helloと出力するコンポーネントを作成し複数回利用します。
[jsfiddle](https://jsfiddle.net/)で実際に記述しながら読むことをおすすめします。


```html:index/html
<div id="app">

  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
  
</div>
<script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

```javascript:index.js
Vue.component( 'hello-component',{
  template:'<p>Hello</p>'
})

var app = new Vue({
  el:'#app',
})
```
実行結果
[![Image from Gyazo](https://i.gyazo.com/eefcc4003154c7f7e14441b33e8f0e8a.png)](https://gyazo.com/eefcc4003154c7f7e14441b33e8f0e8a)

コンポーネントの記述は第１引数にコンポーネント名を第２引数に処理内容を記述しましょう。

グローバル登録されたコンポーネントはすべてのVueインスタンスから利用可能です。

##ローカル登録
特定のVueインスタンス配下でのみ利用するコンポーネントは
ローカル登録によってスコープ（範囲）を狭めることができます。

さきほどグローバル登録したコンポーネントをローカル登録に変更します。

```html:index/html
<div id="app">

  <hello-component></hello-component>
  <hello-component></hello-component>
  <hello-component></hello-component>
  
</div>
<script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

```javascript:index.js
var helloComponent = {
  template:"<p>Hello</p>"
}

var app = new Vue({
  el:'#app',
  components:{
    'hello-component':helloComponent
  }
})
```
実行結果
[![Image from Gyazo](https://i.gyazo.com/eefcc4003154c7f7e14441b33e8f0e8a.png)](https://gyazo.com/eefcc4003154c7f7e14441b33e8f0e8a)

まず使用するVueインスタンスよりも先にコンポーネントを定義します。
次にvueインスタンス内のコンポーネントオプションにプロパティとして設定しましょう。

##コンポーネント名
コンポーネント名を登録する際はハイフンを１つ以上含む（ケバブケース）必要があります。

例）
・hello-component　OK
・button-counter　OK
・hello　NG
・helloComponent　NG　キャメルケースのためNG

理由は既に存在するHTMLや将来定義されるHTML要素との衝突を避けるためです。

ただしVue.jsのスタイルガイドではパスカル記法（全ての単語の先頭が大文字）を
推奨していますので状況に応じて混在しないように使い分けることをおすすめします。
[Vue.jsスタイルガイド](https://jp.vuejs.org/v2/style-guide/index.html#%E8%A4%87%E6%95%B0%E5%8D%98%E8%AA%9E%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%90%8D-%E5%BF%85%E9%A0%88)

```html:index/html
<!--NG記述です-->
<div id="app">

  <hello></hello> 
  <hello></hello> 
  <hello></hello> 

</div>
<script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>

```

```javascript:index.js
//NG記述です
var helloComponent = {
  template:"<p>Hello</p>"
}

var app = new Vue({
  el:'#app',
  components:{
    'hello':helloComponent
  }
})
```

※実行結果として動作しますが、仮に将来helloというタグがHTMLで定義された場合は衝突します。

##コンポーネントのオプション
ボタンを押すとクリック数をカウントするコードをコンポーネントで記述しましょう。

```html:index/html
<div id="app">

  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
  
</div>
<script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

```javascript:index.js
Vue.component('button-counter',{
  data:function(){
    return {
      count:0
    }
  },
  template:'<button v-on:click="count++">{{ count }}</button>'
})

var app = new Vue({
  el:'#app'
})
```
実行結果
[![Image from Gyazo](https://i.gyazo.com/167231ecdf1eee81cba761ba8ab6775e.gif)](https://gyazo.com/167231ecdf1eee81cba761ba8ab6775e)

それぞれのボタンはカウント数を独自に保持しています。
これはコンポーネントを作成するたびに新しいインスタンスが作成されるためです。

コンポーネントのdataは直接定義されず関数となる必要があります。

```javascript:index.js
//例　直接定義
data:{
  count:0
}

//例　関数定義
data:function(){
  return{
    count:0
  }
}
```

理由は各インスタンスが独自にオブジェクトを利用できるようにするためです。

またテンプレートのルート要素は単一である必要があります。
上記のカウンターに"count:"のラベルをつけてみましょう。

```html:index/html
<div id="app">

  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
  
</div>
<script src ="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

```javascript:index.js
Vue.component('button-counter',{
  data:function(){
    return {
      count:0
    }
  },
  template:'<span>count:</span><button v-on:click="count++">{{ count }}</button>'
})

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

実行結果
[![Image from Gyazo](https://i.gyazo.com/68afdcbc4eef1e103f1838d3b1b9e668.png)](https://gyazo.com/68afdcbc4eef1e103f1838d3b1b9e668)
[![Image from Gyazo](https://i.gyazo.com/0ce8f66ef841672801ccb91916a84424.png)](https://gyazo.com/0ce8f66ef841672801ccb91916a84424)

上記の記述ではspanとbuttonで要素が複数になっているためコンパイルエラーが発生します。
この場合はdivタグなどで囲んで要素を１つにまとめる必要があります。

```javascript:index.js
//テンプレートを修正
Vue.component('button-counter',{
  data:function(){
    return {
      count:0
    }
  },
  template:'<div><span>count : </span><button v-on:click="count++">{{ count }}</button></div>' //divで囲んで単数化
})

var app = new Vue({
  el:'#app'
})
```
実行結果
[![Image from Gyazo](https://i.gyazo.com/558e97051de8716ba95107124ad01d85.gif)](https://gyazo.com/558e97051de8716ba95107124ad01d85)

##コンポーネント間の通信
複数のコンポーネントを並列または入れ子に配置された場合
スコープ（データの有効範囲）という概念が発生します。

コンポーネントで定義された情報（データ、メソッドなど）に
アクセスできるのはそのコンポーネントだけということになります。

次回はトランジションです。
[Vue.js　トランジション](https://qiita.com/fumiya0414/items/25d79375a5d39cba0b34)