1. はじめに
はじめまして、1年半前に入社したベトナム人のドゥックです。
最近担当しているプロジェクトでVueを導入しました。使えば使うほどVueは便利だと思いますが、あることでずっと悩んでいます。それはVueのコンポーネント間でのデータの受け渡しのことです。親から子にデータを渡すためにはprops
を、逆に子から親にデータを渡す時は$emit
を使用します。この方法だと構造が一階層の時は問題ないですが、深くネストコンポーネント構造であればデータを共有するのが面倒になってきます。
何か良い方法があるかと探していました。先日先輩に相談してみるとFluxライクなライブラリVuexについて教えてくれました。聞いた瞬間に「これだ!!」と思いました。その時から Vuexを勉強して実際に導入してみました。結果はすごく良くてデータ共有が楽になりました。なので、この記事で皆さんにVuexについてを紹介させていただきたいと思います。
2. Vuexとは
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。
Vuexはネストの深いコンポーネント間でのデータの受け渡しをいい感じにしてくれます。
今まで
改善後
Vuexのコアコンセプト
- ステート(State): ストアで管理している状態で、コンポーネントにおけるdataです。
- ゲッター(Getter): ストアのステートを取得するための算出データです。コンポーネントでいうcomputed的なものです。
- ミューテーション(Mutation): ストアのステートを変更する方法です。
setter
だと考えても大丈夫です。ミューテーションは同期的でなければなりません。 - アクション(Action): アクションは状態を変更するのではなく、データの加工や非同期処理を行い、その結果をミューテーションを使用してコミットします。
3. 実際にVuexを使ってみる
下記はすでに作った簡単なカウンターアプリにVuexを導入してみます。Vuexのメリットをはっきり見れるためまず導入前のコードの状態を見せます。
導入の前
counter_button.vue
カウンターボタンのコンポーネントです。ボタンをクリックするとcount
変数の値が1増えてきます。
<template> <button @click="count_up">Count Up</button> </template> <script> export default { data: function () { return { count: 0 } }, methods: { count_up: function () { this.count++; this.$emit('count_up', this.count) } } } </script>
show_count.vue
カウンター数値(count
の値)を表示するコンポーネントです。
<template> <div>{{count}}</div> </template> <script> export default { props: { count: Number } } </script>
counter_app.vue
親のコンポーネントです。上記の2子コンポーネントをインポートして出します。
<template> <div> <counter_button @count_up="count_up"></counter_button> <show_count :count="count"></show_count> </div> </template> <script> import CounterButton from "./counter_button"; import ShowCount from "./show_count"; export default { data: function () { return { count: 0 } }, methods: { count_up: function (count) { this.count = count } }, components: {show_count: ShowCount, counter_button: CounterButton} } </script>
main.js
このファイルでVueのインスタンスを初期化します。
import Vue from 'vue/dist/vue.js' import CounterApp from './counter_app' new Vue({ el: '#counter-app', components: { counter_app: CounterApp } });
毎回ボタンをクリックしたらcount
の値が1増えてきて$emit
でcounter_button
からcounter_app
に渡します。そしてprop
でcount
をcounter_app
からshow_count
に渡して表示します。今後別のデータを共有したいならまた$emit
toprop
を利用しないといけないですね。この面倒を回避するためVuexを導入して次の項目の状態になります。
導入の後
store.js
VuexのStoreを初期化するファイルです。共有したいデータはStoreのステートになります。count
ステートを変更できるためにincrement
ミューテーションを追加します。
import Vue from 'vue/dist/vue.js' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state){ state.count++ } } }) export default store
counter_button.vue
<template> <button @click="count_up">Count Up</button> </template> <script> export default { methods: { count_up: function () { // $emitの代わりにStoreのアクションを使う this.$store.commit('increment') } } } </script>
show_count.vue
<template> // Storeのステートを紐づく <div>{{this.$store.state.count}}</div> </template> <script> export default { // propがなくなる } </script>
counter_app.vue
<template> <div> <counter_button></counter_button> <show_count></show_count> </div> </template> <script> import CounterButton from "./counter_button"; import ShowCount from "./show_count"; export default { components: {show_count: ShowCount, counter_button: CounterButton} } </script>
main.js
import Vue from 'vue/dist/vue.js' import CounterApp from '../components/counter_app' import store from '../components/store' new Vue({ el: '#about-page', // Storeをローカルに登録する store, components: { counter_app: CounterApp } });
見ての通りshow_count
のprops
がなくなりました。かわりにthis.$store.state
でStore
からデータを紐づけます。emit
の代わりにthis.$store.commit
でStore
のmutation
を使ってcount
を変更できます。そうするとcounter_app.vue
でコードがスッキリになって今後新しいデータを共有したい時に単にStore
に追加してコンポーネントの中で紐付ければすぐに使えます。すごく便利じゃないですか?
4. 終わりに
Vuexを使うことで状態管理ができ、propsと$emitを繰り返し使用するといった面倒から開放されました。実際に担当しているプロジェクトで導入するため2日間Vuexを勉強して、6時間導入して合計が22時間ぐらいです。これからの便利さに考えるとこれは必要なことだと思いませんか?
皆さんも是非Vuexを使ってみてください!!!