LoginSignup
167
138

More than 5 years have passed since last update.

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

Posted at

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

167
138
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
167
138