Vue.js + Vuexでデータが循環する全体像を図解してみた

2019年の2月まで表参道のプログラミングスクールでVue.js(+Nuxt.js)を教えていました。
とある生徒に「Vue.jsとVuexの関連がわからないので図解して欲しい」と頼まれてホワイトボードにサクッと書いて説明してみたらわかりやすいと大好評。
しかもその生徒が私のラフ図をキレイな動画に作り直してくれたのでカットごとの紹介記事を書くことにしました。

![0.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/135e7296-1c0c-78fa-0186-939eb284a7f0.jpeg)

## この記事でわかること
state/mutations/commit/actions/dispatchの違いがわかるようになります。
getterやsubscribeについては扱いません。
実際のコードの書き方にも触れません。
Vuexを使ってはいるけど理解できなくて苦しんでいる方向けの内容です。

## なぜVuexを使うのか
Vuexの図解に入る前に、私がVuexを使っている理由を説明しておきます。
Vuexのメリットが明確でないと図があっても理解が進まないですからね。

Vue.jsに限らず、コンポーネント指向のMVCライブラリは中～大規模開発を想定しています。
ちょっとしたサイトに動きを与えるぐらいなら今でもjQueryが圧倒的に便利です。
大規模開発になるとコンポーネントの数は100個を超えることも珍しくありません。

ルートコンポーネント＞子コンポーネント＞孫コンポーネント＞ひ孫コンポーネント＞...
と続いていくと、今自分が開発しているコンポーネントが何層目にいるのか全くわからなくなります。
この現象はまるで映画「インセプション」の世界。夢の中の夢に入り続けるうちにどこが現実かわからなくなるあれです。

インセプションなVue.jsの世界で主要なデータを標準機能のpropsと$emitでやりとりすることを想像してください。
![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/7f7d29d0-d9ec-6b92-3b0b-a8efb0b9fca1.jpeg)

このくらいシンプルな構造なら問題ないですが先祖から子孫へ、何世代にも渡ってデータを受け渡し、子孫から先祖へ何世代も遡ってイベントを通知することを考えたら地獄絵図です。

![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/9a3ce396-5bee-f303-14b6-b3cf4598fa54.jpeg)


そんな「データとイベントのバケツリレー地獄」から脱するためにVuexを使っています。
「自分が何層目にいるのか？」を把握しながらコーディングしようとするのは無意味です。
映画ではディカプリオがコマ？を回して現実にいるかどうかを確かめていましたが、我々は何層目にいようがどうでも良い。

逆に言うと、コンポーネントが少ない場合は恩恵がないかもしれません。
本やサイトのVuexサンプルを叩いてもメリットがわからない原因は多分そのアプリが小規模だからです。
この記事を読み終わったらぜひ、Vuexのパワーを発揮できるスケールのウェブアプリを開発してみてください。

## Vuex図解
### ①アプリ1つに対してStoreも1つ
![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/9b327701-e4e0-1bd1-d0ff-5353243d8edc.jpeg)

ページではなくアプリ単位で考えます。
vue-cliやNuxt.jsを使っている場合はディレクトリ全体で1つのアプリという構成になっているはずです。

### ②データの変更は必ずmutationを経由
![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/8ba873e5-de23-86d4-f759-69e65a40c6cd.jpeg)

mutationsに登録した関数を呼ぶために使うのが「$store.commit()」です。
storeのmutationsに登録した関数以外の場所でstateを変更してはいけません。

![6.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/24ac60fa-77a2-495f-35b3-a4eaacf572a7.jpeg)

正確な例えではありませんが、\$store.commitは\$emitに似たような役割を担っています。
子が$emitしたら親コンポーネント側で呼んでいたmethodsに相当するのがmutationsのようなイメージです。

### ③storeに保存したデータはstateから読み取る
![7.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/284427a7-00a7-5ab7-6b4a-6fc995265038.jpeg)

あらゆるコンポーネントが\$storeにアクセスできます。
データの読み取りは$store.stateで。
②で説明した通り、読み取ったstateを直接書き換えてはいけません。

### ④mutationは同期的にstateを書き換える
![8.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/597c95c2-818a-b853-d097-92de045c1096.jpeg)

つまり、\$store.commit()を呼んだ次の行でstateは期待通りに変更されています。
mutations内でajax通信のあとにstateを書き換えるような処理をしてはいけません。

### ⑤非同期にstateを書き換えたいならactionを使う
ajaxやsetTimeoutやFirebaseのような非同期処理の後にstateを書き換えたい場合にはactionsに非同期の関数を登録します。

![9.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/214ec228-12d0-1e30-ea73-ec9ffc8ed116.jpeg)

actionを呼び出すときには\$store.dispatchを使います。

![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/16b6baab-7ad5-4b6c-9e52-b0e4e19ff186.jpeg)

非同期処理が終わっても、actionの関数内でstateを書き換えてはいけません。
必ず、\$store.commitを呼びmutationを経由させます。
当然1つのactionのためだけにmutationを1つ余計に登録することもあります。

![11.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/71b535ac-448f-3798-62ec-502ec532592f.jpeg)

非同期処理を扱うので、actionはasync/awaitを使うと便利です。
Promiseではない非同期関数を扱うときは通常のcallbackで書きます。

![12.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/31eeffaa-6472-a97e-3c01-a81a8b64667b.jpeg)

### ⑤Vuexはデータの循環経路を整備してくれる
![13.jpg](https://qiita-image-store.s3.amazonaws.com/0/63528/62cfa4eb-0647-d748-9186-417a388dc11e.jpeg)

props/$emitはデータがコンポーネントの層を上下に移動するイメージでした。
Vuexを使うと他のコンポーネントとの入れ子構造を気にすることなくシンプルな経路でデータを読み書きできるようになります。

## いつdata/props/$emitを使うのか
Vuexがあってもdata/props/$emitを使う場面はたくさんあります。

- そのコンポーネントでしか使わないデータ
- セレクトボックスやモーダルウィンドウの開閉のような揮発性の情報
- 親子間2層だけで情報を受け渡す
- 単機能のコンポーネントを使う、作る

上記の場合でも何らかの方法(localStorageやFirebase)で該当のデータを永続化する必要があるならVuexを使いたいですね。

慣れると非常に便利な機能なんですが、常に全てのデータをVuexで管理すれば良いというわけではありません。
あらゆる機能には適材適所があり、アプリの規模、チームの技術力、コンポーネントの数によってもVuexを使うかどうか判断すべきです。

## 動画を作ってくれた人
私のVue.js講座の聴講生です。江戸川区でフロントエンジニアをやっているようです。

[![アイコン画像](https://qiita-image-store.s3.amazonaws.com/0/13713/profile-images/1515631749)](https://qiita.com/shiba_mitue)
@shiba_mitue 

オリジナル動画はTwitterにアップされてます。
[Vue.jsのコンポーネントとVuexの処理の流れを40秒のアニメーションに図解しました](https://twitter.com/shiba_328/status/1023886060852596738)

## 筆者の近況
TypeScript製OSSの開発とNuxt.jsを楽しんでいます
2019年のNuxt.jsアドベントカレンダー2日目を担当したのでTSやってる方はこちらの記事もどうぞ！
[HTTPリクエストを型安全にする手法とNuxt TSでの実装例](https://qiita.com/m_mitsuhide/items/c0cce7f3a79907c6e75c)