なんでVue.jsではコンポーネント内のデータは関数で書くのか？

こんにちは、[Yui](https://twitter.com/yui_active)です。

私はVue.jsを使って業務委託で開発を行っているのですが、最初ナニモワカランという状態から参入させてもらったため、Vue.jsを雰囲気で書いてました。（今もだいぶ雰囲気で書いちゃってるので、徐々に精進していきます。）

**コンポーネント内ではデータは関数で定義する。**
Vueを書く人からしたら当たり前かもしれませんが、そのへんも理屈を全くわからないまま**そういうもん**だとおもって開発してました。
ようやく最近そのへんの理屈がわかるようになってきたので、今回はそのことについて書きます。

参考：[超Vue.js 完全パック - もう他の教材は買わなくてOK！ (Vue Router, Vuex含む)](https://ntteast2.udemy.com/course/vue-js-complete-guide/learn/lecture/15371192#questions)

#そもそもコンポーネントとは？
コンポーネントとは、**何回も使える要素**のことを指します。

コーディングをしていると、再利用したい要素というのが出てくるかと思うのですが、その度に新しくコーディングするのは大変です。

例えば、下記のコードを見てください。

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

new Vue({
    el:'#app',
    data: {
        hello: 'hello'
   },
   template: '<p>{{hello}}</p>'
})
```

この場合、出力は`hello`と出力されますが、もしこれを複数回表示させたい場合、どうなるでしょうか？
つまり、'hello'を何回も表示させたい場合です。

通常で考えれば、

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

new Vue({
    el:'#app',
    data: {
        hello: 'hello'
   },
   template: '<p>{{hello}}</p>'
})
```

このように、`<div id="app"></div>`の部分を何回も書けばいいと思うかもしれません。
ただ、Vue.jsでは実はこれでは一回しか表示がされないのです。

なぜかというと、Vueインスタンスでは同じものは表示しないという性質があるからです。
そこでコンポーネントの出番です。

というわけで、下記のように書き直します。

```vue:
<div id="app">
  <component></component>
  <component></component>
  <component></component>
</div>

Vue.component('component', {
    data: function() {
        return {
    	   hello:'hello'
        }
     },
    template: '<p>{{hello}}</p>'
})

new Vue({
    el:'#app' 
})
```
こうすることで、出力結果は

```
hello
hello
hello
```
となります。

コンポーネントでは、最初に第一引数でコンポーネント名を定義する必要があります。
今回は簡単に`component`としたので、そのコンポーネントを表示するときは`<component></component>`と書きました。

これを例えば

```
Vue.component('hello_component', {
    data: function() {
        return {
    	   hello:'hello'
        }
     },
    template: '<p>{{hello}}</p>'
})
```
このようにした場合は、出力する際に`<hello_component></hello_component>`と書くことができます。

#コンポーネント内のデータを無理やりオブジェクト形式で書いてみる
それでは本題の、なぜコンポーネント内の`data`部分は関数で定義するのかに関して。

そもそも、オブジェクト形式では書けないのでしょうか。
書いてみましょう。

```vue:
Vue.component('component', {
    data: {
        hello:'hello'
    },
    template: '<p>{{hello}}</p>'
})

new Vue({
    el:'#app' 
})
```
出力しても何も表示されません。
vueではコンポーネント内では関数を使わないとエラーになるみたいですね。
ただ、data部分をオブジェクト形式で無理やり書くことは出来るので、書いてみます。

```vue:
var data = {
    hello: 'hello'
}

Vue.component('component', {
    data: function() {
        return data
     },
    template: '<p>{{hello}}</p>'
})

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

無事出力されました！

#何が問題なのか

上記のように、書いた場合、何が問題なのでしょうか？
表示自体は問題なくできているように感じます。

ただ上記の場合だと`var data`で中身となるデータ部分を一定のものに定義してしまっています。
つまり、**今後データが変更されたらすべての部分で変更が起こってしまう**ということです。

具体的なコードを出します。

```vue:
Vue.component('component', {
    data: function() {
        return data
     },
    template: '<p>{{hello}}<button @click="bye">bye</button></p>',
    methods: {
        bye: function() {
            this.hello = 'bye';
        }
    }
})

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

このような形で、ボタンを作ってアクションを起こした場合、
![4dfc8556864bb9f83614124d5b87af8c.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670446/21b6c65d-145a-2de2-9c70-e8ec4a0b86c5.gif)

こんな感じで、全部のデータ（hello）が`bye`に変わってしまうんです。

ただ、できればボタンを押したところだけのデータを変えたいですよね。
というわけで前置きが長くなりましたが、やはりきちんとデータは関数で書いてあげます。

```vue:
Vue.component('component', {
    data: function() {
        return {
    	   hello:'hello'
        }
     },
    template: '<p>{{hello}}<button @click="bye">bye</button></p>',
    methods: {
        bye: function() {
            this.hello = 'bye';
        }
    }
})

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

こうすることで、きちんとデータが独立して変化するようになりました！

![2db813a76c114ac2b4cc1f32002b4523.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670446/c2630449-0c61-217d-347f-c2f624de9d59.gif)

#まとめ

というわけで、なぜVue.jsではコンポーネント内のデータは関数で書くのか？に対する答えとしては、`レイアウトなどは同じでも、データをそれぞれで変化させる必要があるから`ということになります。

そもそも、いつコンポーネントを使うのかと考えた際に、レイアウトは同じでも、中に入ってるデータまで全く同じ表示をすることをないですよね。データだけを変更できるように再利用可能にしたものがコンポーネントです。

当たり前のようですが、あまり意識せずに使っていたのでまとめてみました。