Vue.js Advent Calendar 2016 14日目です。
Vue2 + Vuex2の組み合わせはVueを正しくfluxに導き、より巨大なアーキテクチャを管理できるようになった。
逆に言うとVueの気軽さが失われてしまったのでは、という印象を持つ方もいらっしゃるのでは。
しばらく使ってみたら肩の力の抜き方が分かってきたので、構造の複雑さ別に書き方をまとめてみる。
このポストは
"vue": "^2.1.4"
"vuex": "^2.0.0"
の提供でお送りします。
Level 1
子コンポーネントとか存在しない。単一のVueコンポーネントで完結する。
勿論Vuexも要らない。
new Vue({
data: {
hello: false
},
methods: {
sayHello () {
this.$emit('hello')
}
},
created () {
this.$on('hello', () => {
this.$data.hello = true
})
}
})
イベントを介さずお互いにthis.methodName()で呼び出しても良い気もする。
Level 2
いくつかの子コンポーネントが存在するが、Vuexは使わない。
v-on
ディレクティブによって子コンポーネントからイベントを親に伝達することができる。
<hello-component @my-event="onHelloEvent"></hello-component>
ちょっとした子→親へのイベント伝達ならこれでも良いが、スケールしづらい。
Level 3
いくつか子コンポーネントが存在し、何層かの構造が出来つつある複雑さ。
管理するためにVuexを導入してみる段階。
モジュールのないStore程度なら以下の通り適当でもやっていける。
Vuex exampleだとtodomvcの書き方が丁度良いのではないでしょうか。
// 基本的なStore部分
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
hello: true
},
mutations: {
bye (state) {
state.hello = false
}
}
})
Getters と computed
複数コンポーネントでGettersを使い回す予定が無いなら、getters
に書かずにcomputed
で宣言してもいい。
import * as types from './store/types'
export default {
computed: {
hello () {
return this.$store.state.hello
}
}
}
Actions と methods
methods
の中でcommit
も邪道ではない。
StoreにActionsを書かなくてもやっていける。
methods: {
sayHello () {
this.$store.commit('hello')
}
}
更に、Mutationsをmethodsの中に呼び出せる。
こんがらがったらちゃんと分割した方が良さそう。
methods: {
sayHello () {
this.$emit('hello')
},
...mapMutations([
'bye'
])
}
<button @click="bye">BYE</button>
適当さ加減の原動力は「ストアとコンポーネントの行き来が面倒」に尽きる。
Level 4
複雑にデータもコンポーネントも重なっていてヤバい。
Gettersで出力を管理し、
Mutationsもファイルに分けて書くし、
MutationTypesもモジュールごとに定数で管理する。
モジュール分割や命名など、要件によって変わると思うので個人的に頻出するものを書く。
名前空間
モジュールごとに名前空間を付けないと管理しきれない。
const namespace = 'hello/'
export const HELLO = namespace + 'HELLO'
export const SAY_HELLO = namespace + 'SAY_HELLO'
export const TOGGLE_DONE = namespace + 'TOGGLE_DONE'
import * as types from './types'
export default helloModule = {
state: {
hello: false,
},
getters: {
[types.HELLO] (state) {
return state.hello
}
},
actions: {
[types.SAY_HELLO] (context, payload) {
api.sayHello()
.then((data) => {})
.catch((err) => {})
}
},
mutations: {
[types.HELLO] (state, payload) {
state.hello = true
}
}
}
actionsもgettersも定数になるのは個人的に気持ち悪いのですが、
多分これが一番混乱しないと思います。
Data と State
export default {
data () {
isLoading: false,
isVisible: false
}
}
個々のコンポーネントが持つdata
の使い所とは。
「読み込み中はローダーを表示する」時、StoreにViewのためにisLoading
が生えるのは宜しくない。
methods
上でquerySelector()
するよりは、data
にisLoading
を生やして、v-class
で状態管理した方がよさげ。
個人的には、アプリケーションとしてのデータに関係ないが見た目を管理したい時はmethods
とdata
で管理し、データのロジックはActionsとMutationsに任せるよう分けている。
まとめ
ドキュメントや使ってみたでは行儀の良い書き方が書かれていて「Vue2.0で書き方が複雑になってるのでは」と尻込みしてしまうこともあるかもしれないが、慣れてくるとそんなこともなかった。
個人レベルでLevel 4までガッチガチに設計固めて作る必要も無いので、程度に肩の力を抜いてVuexを楽しんでほしい。
Vueは2.0でも適当に書いてなんとかなるフレームワークです。