Vueのディレクトリの責務について考える

vue-cliで生成した状態で`src`以下はこうなっています。（assetsは省略）

```
├── components // コンポーネント
├── views // ページと1:1
├── App.vue // トップページのコンポーネント
├── main.js // プラグインの使用など
├── router.js // ルーティング
└── store.js // Vuex
```

もちろんこのままゴリゴリ書いていくことも可能ですが、`components`以下にロジックが大量にかかれ、共通化されてない見通しの悪いコードが生成されることになります。

- [components/Directory.vue](https://github.com/matometaru/vue-study/blob/c6661abebf00e759211795a640b0557b3875cb00/src/components/Directory.vue)

## コンテナコンポーネントを追加する

コンテナコンポーネントを入れる`containers`を追加しました。

```
├── components // コンポーネント
├── containers // コンテナコンポーネント
├── views // ページと1:1
├── App.vue // トップページのコンポーネント
├── main.js // プラグインの使用など
├── router.js // ルーティング
└── store.js // Vuex
```

コンテナコンポーネントとは何でしょうか？

- Vuexを知っている
- 他のコンテナコンポーネントを呼び出してもよい
- ラッパーのdivを除いて独自のDOMマークアップは書かない

以下の記事がすごくわかりやすいのでぜひ一度読んでみてください。

[2018年Vue.jsとVuexを使ってる人に提案するコンポーネントの分類と設計パターン - Qiita](https://qiita.com/kahirokunn/items/06366888409e73dfe70f)

コンテナコンポーネントにロジックを寄せることで`components`以下ではpropsで値を受け取るだけでよくなり、テストも書きやすくなります。
ですが、ロジックが`containers`以下に移動しただけなので、次は`containers`の見通しが悪くなりました。
ロジックの置き場所を考える必要がありますね。

- [containers/Directory.vue](https://github.com/matometaru/vue-study/blob/4b3a80f17af46d8026815e43770a9aba21db088c/src/containers/Directory.vue)
- [components/UserList.vue](https://github.com/matometaru/vue-study/blob/4b3a80f17af46d8026815e43770a9aba21db088c/src/components/UserList.vue)
- [components/ItemList.vue](https://github.com/matometaru/vue-study/blob/4b3a80f17af46d8026815e43770a9aba21db088c/src/components/ItemList.vue)

## componentsを整理する

ロジックの置き場所を考える前に、`components`以下をpropsで受け取ったコンポーネントの書き方のポイントを紹介します。

- v-model
- emit or callback

### v-modelは糖衣構文

v-modelと書くだけでテキストをリアクティブなデータを扱えるのはすごく便利なのですが、`v-bind`、`v-on`、`v-model`とか最初の方は混乱しますよね。v-modelは糖衣構文なので`v-bind`と`v-on`を使って書き換えることができます。（`:`はv-bind、`@`はv-onの省略記法）

```
<input v-model="text">
```

↓

```
<input :value="text" @input="text=$event.target.value">
```

### emit or callback

どちらも子から親に変更を伝えることができます。（親からpropsで受け取った値は基本的に子側で変更できないので、変更を伝える必要があります。）
最初は`emit`を使ってたのですが、Vue固有の書き方なので少しコードを書かないと使い方を忘れることがよくありました。`callback`ならVueが初めての人も分かる書き方なので個人的にはコールバック方式をオススメします。

`v-model`と`emit or callback`を理解しておくことで、コンポーネントには一切の状態(data)を持つことなく、propsから値を受け取るだけのシンプルなコンポーネントになります。（ステートレスコンポーネント）

以下のItemList.vueは親から受け取った`filter.userId`をv-modelで指定していますが、オブジェクトのプロパティなので子側でも書き換えができます。

[components/ItemList.vue](https://github.com/matometaru/vue-study/blob/4b3a80f17af46d8026815e43770a9aba21db088c/src/components/ItemList.vue#L6)

## API通信を分離する

現在は`containers`でaxiosをimportしているので、テスト時にaxiosをモック化する必要があります。また、axios以外のライブラリを使うことになった場合など変更が容易ではありません。
以下の記事を参考に`repositories`を作成します。

[【Vue.js】Web API通信のデザインパターン (個人的ベストプラクティス) - Qiita](https://qiita.com/07JP27/items/0923cbe3b6435c19d761)

今回はQiitaのAPIをコールするだけなので少し簡略化しています。また、axiosを知っているのは`repositories`以下のみにしたいのでAxiosPromiseを返さずPromiseを返しています。

[src/repositories/qiitaRepository.ts](https://github.com/matometaru/vue-study/blob/15531e524fcde1e1a14ac09682308b87afbc1ce9/src/repositories/qiitaRepository.ts)

[containers/Directory.vue](https://github.com/matometaru/vue-study/blob/15531e524fcde1e1a14ac09682308b87afbc1ce9/src/containers/Directory.vue)


コンポーネントからAPIを呼ぶコードのテストではAxiosMockなどを使わずに`qiitaRepositories`をsinonなどでstub化すればいいかと思います。

## ここまでのディレクトリ構成

```
├── components // ステートフルコンポーネント、ステートレスコンポーネント
├── containers // コンテナコンポーネント
├── repositories // API通信
├── views // ページと1:1
├── App.vue // トップページのコンポーネント
├── main.js // プラグインの使用など
├── router.js // ルーティング
└── store.js // Vuex
```

今回はこれで終わりです。

次回はservicesとmodelsを追加したパターンを紹介したいです。

[Vueのディレクトリの責務について考える（2） - Qiita](https://qiita.com/kitanote/items/c9534f48fddabb349617)