Vue.jsってどうやったら綺麗に使えるのよ

# 前置き
・LaravelついでにVue.jsを使うことになった
・公式のドキュメントを流し読み
・とりあえずコンポーネントで分けてみるか
・カオスwwww

というわけで、根本的にどうVue.jsを使っていいか分かっていないようなので、
どうすれば「俺はVue.jsを使ってるぜっ！」
と言えるのか試行錯誤してみる。

# 事の始まり
## 簡単な検索ページでも作ってみる
<img width="339" alt="スクリーンショット 2017-09-22 14.56.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/f1235670-aa51-7dda-8e97-1cb6e6497902.png">

```html
<div>
    id<input type="text">
    name<input type="text">
</div>
<div>
    <input type="button" value="検索">
</div>
<br />
<div>
    <table border="1">
        <tr>
            <th>id</th>
            <th>name</th>
        </tr>
        <tr>
            <td>1</td>
            <td>たかし</td>
        </tr>
    </table>
</div>
```

## どうコンポーネント分けようかな
こ・・こんな感じ？

<img width="344" alt="スクリーンショット 2017-09-22 15.02.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/42bd9479-c271-a70d-366b-03b38f2231e4.png">

とりあえずツリー風に書いて、役割を明確に・・

<img width="558" alt="スクリーンショット 2017-09-22 15.16.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/0b8d89ba-6d62-95d8-b253-ae79bcb7a563.png">

▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああああ

## クールになるんだ
OK落ち着け俺。
何か根本的に間違っている。

情報を整理してリトライしよう。

# Vue.jsはMVVM
## (M)odel
データだったり、ロジックだったり。

Vue.jsでいうと、dataオブジェクトやmethodsのこと。

props、computed、watchもModelに入るのかな？

## (V)iew
DOM要素のこと。

Vue.jsでいうと、elのこと。

templateやrenderもそうなのかな？

## (V)iew(M)odel
ModelとViewの仲立ちをもの。

Vue.jsでいうと、Vueインスタンスのこと。

## 考察
Vueインスタンスにdataオブジェクトを内包できるから勘違いしたような気がする。
**→明確にViewModelとModelを分けてみよう。**

ViewModelであるVueインスタンスと、Modelは切り離すべきではなかろうか（￣ﾍ￣）ｳｰﾝ
**→用意するModelは、入力と結果の二つってところか。**

検索ロジックってどっちに入れよう・・。
入力値も使うし、結果も格納しないといけないんだよな・・。
**→ロジック用のModelを用意してみる。**

データ用のModelにはSetterGetterを用意すればいいな。
んで、ロジックModelで入力と結果のModelに入れ込めばいいんだ。

<img width="554" alt="スクリーンショット 2017-09-22 16.00.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/d726cf9e-95a2-fb0e-133b-1d67228fb64d.png">

こんなところか。

Viewに関しては、コンポーネントの分け方の問題なのかなぁ・・。

# コンポーネント
## ぼくのかんがえるこんぽーねんと
なんか・・部品・・ですよね・・。

はい、この曖昧さが悲劇を生んだような気がします。

## 公式より
```
基本的な HTML 要素を拡張して再利用可能なコードのカプセル化を助けます。
高いレベルでは、コンポーネントは Vue.js のコンパイラが指定された振舞いを加えるカスタム要素です。
場合によっては、特別な is 属性で拡張されたネイティブな HTML 要素の姿をとることもあります。
```

使い回しが聞きやすいように、独自タグ作るやーつ。
と読める。

使い回しが効きそうなところはコンポーネントにしましょう。
サイトのデザインって統一感持たせたいんだから、
言ってしまえば全部使い回しが効くように作るべきだよね。

じゃあ
**「使い回しが効く部品」**
で考えることにしよう。

## コンポーネント再検討 
<img width="383" alt="スクリーンショット 2017-09-22 16.41.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/6757c82a-ec4d-ed07-8cfe-2f1792315ec8.png">

・画面全体のテンプレート
・入力部(ラベル＋テキストボックス)
・ボタン
・テーブル

に分けてみました。

# 全体像を再検討
<img width="667" alt="スクリーンショット 2017-09-22 16.52.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/24484/f4f811da-9a5d-0f9c-ba1c-b12b05546eb1.png">

コンポーネントとモデルを繋げる線がViewModel(Vueインスタンス)になるのかな・・？

# 作ってみる
## 入力モデル
```js:model_input.js
require('./bootstrap');

window.Vue = require('vue');

module.exports = new Vue({
    data: {
        id: '',
        name: ''
    },
    methods: {
        getId: function() {
            return this.id;
        },
        setId: function(val) {
            this.id = val;
        },
        getName: function() {
            return this.name;
        },
        setName: function(val) {
            this.name = val;
        }
    },
    setMethods: function() {
        return this.methods;
    }
});
```

解説)
特にありません。
再検討したものをそのまま実装してます。

## 検索結果格納用モデル
```js:model_result.js
require('./bootstrap');

window.Vue = require('vue');

module.exports = new Vue({
    data: {
        list: []
    },
    methods: {
        getList: function() {
            return this.list;
        },
        setList: function(val) {
            this.list = val;
        }
    },
    setMethods: function() {
        return this.methods;
    }
});
```

解説)
特にありません。
再検討の時と違うのは、結果は配列で持たせるように変更してます。


## ロジック用モデル
```js:logic_search.js
// 不要としました
```

解説)
検索ロジックはViewModelに依存させました。

なぜか？
多くの値(Model等)を用いたり、返したりするロジックを外出しすると、
ViewやViewModelの記載が複雑になりました。

一意性の高いロジックは外出しせずに、都度その場で定義した方が良さそうです。

```html:複雑になった例
<!-- モデルをコンポーネントに渡さなければならないため、記載や可読性が複雑になる -->
<my-button label="検索" v-on:prop-click="search(modelInput, modelResult)"></my-button>
```

## 入力部コンポーネント
```js:text.vue
<template>
    <span>
        {{label}}<input type="text" v-model="value">
    </span>
</template>

<script>
    export default {
        props: ["label"],
        data: function() {
            return {value: ""};
        },
        watch: {
            value: function(val, oldval) {
                this.$emit("prop-setter", val);
            }
        }
    }
</script>
```

解説)
入力値が変わるたびに、親から受けとったModelのsetterを実行。

## ボタンコンポーネント
```js:button.vue
<template>
    <input type="button" v-bind:value="label" v-on:click="click()">
</template>

<script>
    export default {
        props: [ "label" ],
        methods: {
            click: function() {
                this.$emit("prop-click");
            }
        }
    }
</script>
```

解説)
ボタンを押したら、受け取ったメソッドを実行するだけ。

## テーブルコンポーネント
```js:table.vue
<template>
    <div>
        <table border="1">
            <tr>
                <th v-for="header in propHeaders">{{header}}</th>
            </tr>
            <tr v-for="data in propDatas">
                <td>{{data[0]}}</td>
                <td>{{data[1]}}</td>
            </tr>
        </table>
    </div>
</template>

<script>
    export default {
        props: [ "prop-headers", "prop-datas" ]
    }
</script>
```

解説)
受け取ったヘッダと値を使ってテーブル表示。

## (app.js)ViewModelの定義等
```js:app.js
require('./bootstrap');

window.Vue = require('vue');

import ModelInput from './model_input.js';
import ModelResult from './model_result.js';
//import LogicSearch from './logic_search.js';

Vue.component('my-text', require('./components/text.vue'));
Vue.component('my-button', require('./components/button.vue'));
Vue.component('my-table', require('./components/table.vue'));

const input = new Vue({
    el: '#input',
    data: ModelInput.$data,
    methods: ModelInput.$options.setMethods()
});

const button = new Vue({
    el: '#button',
    data: {
        modelInput: ModelInput,
        modelResult: ModelResult
    },
    methods: {
        search: function() {
            var result = [];

            result.push([this.modelInput.getId(), this.modelInput.getName()]);
            result.push([2, "たかし"]);
            
            this.modelResult.setList(result);
        }
    }
});

const table = new Vue({
    el: '#table',
    data: ModelResult.$data
});
```

解説)
各コンポーネントがモデルを使用できるように、VueオブジェクトとModel紐づけてます。

## ビュー
```html
<div id="input">
    <my-text label="id" v-on:prop-setter="setId"></my-text>
    <my-text label="name" v-on:prop-setter="setName"></my-text>
</div>
<div id="button">
    <my-button label="検索" v-on:prop-click="search"></my-button>
</div>
<br />
<div id="table">
    <my-table v-bind:prop-headers="['id', 'name']" v-bind:prop-datas="list"></my-table>
</div>
```

解説)
コンポーネントを使って描画。

# まとめ
・最初に比べるとだいぶマシになった。
・誰か参考になる情報やリンクを教えてください。