Posted at
Vue.jsDay 14

構造の複雑さとVuex書き分け

More than 1 year has passed since last update.

Vue.js Advent Calendar 2016 14日目です。

Vue2 + Vuex2の組み合わせはVueを正しくfluxに導き、より巨大なアーキテクチャを管理できるようになった。

逆に言うとVueの気軽さが失われてしまったのでは、という印象を持つ方もいらっしゃるのでは。

しばらく使ってみたら肩の力の抜き方が分かってきたので、構造の複雑さ別に書き方をまとめてみる。

このポストは

"vue": "^2.1.4"

"vuex": "^2.0.0"

の提供でお送りします。


Level 1

子コンポーネントとか存在しない。単一のVueコンポーネントで完結する。

勿論Vuexも要らない。


main.js

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.js

// 基本的な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で宣言してもいい。


app.js

import * as types from './store/types'

export default {
computed: {
hello () {
return this.$store.state.hello
}
}
}



Actions と methods

methodsの中でcommitも邪道ではない。

StoreにActionsを書かなくてもやっていける。


methods

methods: {

sayHello () {
this.$store.commit('hello')
}
}

更に、Mutationsをmethodsの中に呼び出せる。

こんがらがったらちゃんと分割した方が良さそう。


methods

methods: {

sayHello () {
this.$emit('hello')
},
...mapMutations([
'bye'
])
}


html

<button @click="bye">BYE</button>


適当さ加減の原動力は「ストアとコンポーネントの行き来が面倒」に尽きる。


Level 4

複雑にデータもコンポーネントも重なっていてヤバい。

Gettersで出力を管理し、

Mutationsもファイルに分けて書くし、

MutationTypesもモジュールごとに定数で管理する。

モジュール分割や命名など、要件によって変わると思うので個人的に頻出するものを書く。


名前空間

モジュールごとに名前空間を付けないと管理しきれない。


modules/hello/type.js

const namespace = 'hello/'

export const HELLO = namespace + 'HELLO'
export const SAY_HELLO = namespace + 'SAY_HELLO'
export const TOGGLE_DONE = namespace + 'TOGGLE_DONE'


modules/hello/index.js

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


ex.js

export default {

data () {
isLoading: false,
isVisible: false
}
}

個々のコンポーネントが持つdataの使い所とは。

「読み込み中はローダーを表示する」時、StoreにViewのためにisLoadingが生えるのは宜しくない。

methods上でquerySelector()するよりは、dataisLoadingを生やして、v-classで状態管理した方がよさげ。

個人的には、アプリケーションとしてのデータに関係ないが見た目を管理したい時はmethodsdataで管理し、データのロジックはActionsとMutationsに任せるよう分けている。


まとめ

ドキュメントや使ってみたでは行儀の良い書き方が書かれていて「Vue2.0で書き方が複雑になってるのでは」と尻込みしてしまうこともあるかもしれないが、慣れてくるとそんなこともなかった。

個人レベルでLevel 4までガッチガチに設計固めて作る必要も無いので、程度に肩の力を抜いてVuexを楽しんでほしい。

Vueは2.0でも適当に書いてなんとかなるフレームワークです。


参考資料

Vuex Docs

Vuex examples