2018-04-19

Nuxtでrouteに応じてVuex Storeをmodule分割する方法

まとめ

  • Nuxt は route に応じて自動で Vuex Store を module 分割できるが、あえて手動で registerModule することもできる
  • module 分割の際に大事なのは
    • namespaced 設定
    • state を function に
    • registerModule はすでに存在する module を上書きしてしまう(壊れる)
  • Store は SSR での扱いに注意が必要。不要なら beforeMount() 以降のタイミングで初期化することでサーバサイドでは無視されるようにすることができる。

Vuex Storeのmodule分割の基本

cf. モジュール · Vuex

基本的な Vuex Store の構造は

state: {
},
mutations: {
},
actions: {
}

となるが、これを以下のようにする。

{
  modules: {
    name: {
      state: {
      },
      mutations: {
      },
      actions: {
      }
    },
    name: {
      state: {
      },
      mutations: {
      },
      actions: {
      }
    }
  }
}

Nuxt.js では store/ 以下に page に対応する module 構造を置いてあげれば自動的に module として登録される。便利。

routeに同期させて同一構造のmoduleを手動でregisterする

手動 register

module 構造の Store を作る方法は上の構造の object をそのまま new Vuex.Store() に突っ込んでもよいのだが、動的に組むには store.registerModule(NAME, OBJECT) してあげるとよい。

Nuxt の場合は特に何の設定もしなくても各 component から this.$store で Vuex Store へアクセスできるので、

this.$store.registerModule(NAME, OBJECT)

になる。

同一構造の module を動的 register する際の注意点

  1. namespaced: true を設定
  2. state は function
  3. module を registerModule するタイミングで state が消える
  4. Vue の SSR が beforeCreate(), created() までしか Lifecycle Hook を利用できないので、beforeMount() 以降で初期化すると client side だけに集中できる

created() で registerModule 以外の $store 操作を行なっている場合は module 生成 1 については同一構造の state が複数あるとどれに対してアクセスしているのかを明示しつつそれぞれの影響範囲が閉じている必要がある。そのための namespaced.

2 については Vue component の data と同じ。インスタンスが複数登場する場合は plain object を使ってしまうと state がみんな同じになってしまう。

3 については分かってしまえばその通りなんだけど、route を手動で書かなくても自動的に設定してくれる Nuxt 環境では注意しないといけない点。

実際にどう対処するか、以下のコードをサンプルとして挙げておく。module name を route name と同一にすると仮定した場合はこのように意図せぬ Store の破壊を防ぎつつ構築していくとよい。

if ( typeof this.$store.state[this.$route.name] == 'undefined' ) {
  this.$store.registerModule(
    this.$route.name,
    {
      namespaced: true,
      state() {
        return {
        }
      },
      mutations: {
      },
      actions: {
      }
    }
  )
}

4 はしばらくハマった。Nuxt で Universal アプリを書くことにした場合、Vue の SSR の知識なしに始めると変なところでハマる可能性があるので注意が必要。

Singleton な Store を Universal アプリで利用するには root component が初期化される前に acyncData で行うのがスジらしい。

今回は Nuxt が最初から用意してくれている Page component に hook して registerModule しようという考えだったので、created() の後に registerModule することで SSR 時には無視されるようにした。

cf. データのプリフェッチと状態 · GitBook

※ なお、困ったことに vue-devtool では Store が破壊されたことは分からない。そこまで中の状態を細かく見ていないようだ。少なくとも v4.1.1 の段階では分からなかった。

About

例によって個人のなんちゃらです