基礎からメモ: Vue.js CH7-p252 Vuexの導入

Vuex は Vueアプリケーションで使うデータとその状態を一元管理する「状態管理」のための拡張ライブラリ。

深くネストされたコンポーネント構造では、コンポーネント間でprops$emitを使って各コンポーネントうしのやり取りをする必要があるが、Vuex を使えばデータ管理がしやすくなる。

Vuexのインストール

まず管理ツールの n で node と npm のバージョンアップから

$ node -v
v10.10.0  # アップ前のバージョン

$ n --lts
12.16.1  # 最新LTS版をインストール

$ sudo n lts

$ node -v
v12.16.1
$ npm -v
6.13.4

練習用プロジェクト作成

$ vue init webpack my-app
$ cd my-app/
$ npm install

Vuex と babel-polyfillをインストール

$ npm install vuex babel-polyfill

npm run dev で開発用サーバを立ち上げ、ブラウザから localhost:8080 にアクセスして初期表示を出しておく。あとは構成ファイルを書き換えるたびにリロードなしで自動的に画面が更新される。ブラウザの開発ツールを表示しておけば、エラーなどもコードを書き換えるたびに出てくる。

シンプルなストア構造

Vuex が状態管理するためのストア(仮想のデータベースのような役割)を作成する。 my-app/src/直下にstore.jsを作成

[src/store.js]

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

export default store

stateオプションに定義されるのはアプリケーションで使用するデータ。これを定義し読み込むことで、このデータにアクセスできる。 ここにあるように Vuexインスタンス内では、Vueインスタンスと違い自身の保持するデータにthisなしでアクセスできる。

したがってメソッド定義にアロー関数が使える。

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => { state.count++ }
  }
})

main.jsからsrc/store.jsを読み込み、store.state.countを利用する。 パスの@はデフォルトで定義されているsrcディレクトリのエイリアス@エイリアスが使えない場合はmain.jsからの相対パス./store.jsでも問題ない。

[main.js]

import Vue from 'vue'
import App from './App'
import store from '@/store.js'

Vue.config.productionTip = false

// countを出力
console.log(store.state.count)
// ミューテーションのincrementをコミットする
store.commit('increment')
// countの値が増えている
console.log(store.state.count)

new Vue({
  el: '#app',
  render: h => h(App)
})

ストアをVueアプリケーションに登録し共有する

この状態では各単一ファイルコンポーネントごとにstore.jsを読み込む必要があるが、main.jsでストアのインスタンスをVueアプリケーションのルートに登録することで、コンポーネントインスタンスプロパティ$storeとして全てのコンポーネントから利用できる。

[main.js]

new Vue({
  el: '#app',
  store,  // <= storeを登録
  render: h => h(App)
})

[src/App.vue の scriptタグ内]

import HelloWorld from './components/HelloWorld'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  created() {
    // countを出力
    console.log(this.$store.state.count)
    // ミューテーションのincrementをコミットする
    this.$store.commit('increment')
    // countの値が増えている
    console.log(this.$store.state.count)
  }
}

同じ出力指示をsrc/components/HelloWorld.vueにも書いてみると、

[src/components/HelloWorld.vue]

export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  created() {
    // ミューテーションのincrementをコミットする
    this.$store.commit('increment')
    // countの値が増えている
    console.log(this.$store.state.count)
  }
}

App.vueHelloWorld.vueで incrementを2回コミットしているので、store.state.countのコンソール出力は

0 → 1 → 2

となるはずだ。コンポーネント間でストアが共有されているのがわかる。