動かざることバグの如し

殿、温めておいたバグがこちらでございます

vuexメモ

自分にはNuxtJSは早すぎた(てきとう

以下のようなsrc/App.vue

<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: 'nyaa',
    };
  },
};
</script>

これは今まで。これをstoreで管理できるように

yarn add vuex

でsrc/main.jsはデフォルトだと以下のようになっているはず。

import Vue from 'vue'
import App from './App.vue'

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

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

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
})

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

に変更

でApp.vueを以下に変更

<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    {{ msg }}
  </div>
</template>

<script>
export default {
  /*data() {
    return {
      msg: 'nyaa',
    }
  },
  }*/
}
</script>

これでstoreから参照できるようになる

事実、今までのようにthis.msgのようにアクセスしようとしても

Property or method "msg" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

とエラーになる。

取得

this.$store.state.msg

変更

this.$store.state.msg = 'changed'

はダメで必ずmutation(操作)を経由しなければならない

さっきのmain.jsのstoreにmutationを追加して

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
  mutations: {
    setMessage(state, payload) {
      state.msg = payload
    }
  }
})

変更するときはコミット

this.$store.commit('setMessage', this.$store.state.msg+'!')

全体で言うと

<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    <button v-on:click="increaseMsg">click</button>
  </div>
</template>

<script>
export default {
  methods: {
    increaseMsg: function() {
      // NG
      /*this.$store.state.msg += '!'*/
      this.$store.commit('setMessage', this.$store.state.msg+'!')
    }
  }
}
</script>

になる。が、実際にはthis.$store.commit()を呼び出すことは殆ど無い。(らしい)

なぜか。それはmutations関数の中は同期的でなければならいから。つまり非同期処理は絶対にかけない。

じゃあ非同期にstoreの処理変更できないやんけ、ってなる。そこでactionsという別の仕組みがある。

Vuex.Store内に以下を追記

  actions: {
    saveMessage(context, payload) {
      context.commit('setMessage', payload)
    },
  }

で呼び出すときは以下

this.$store.dispatch('saveMessage', this.$store.state.msg+'!')

全体図

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

const store = new Vuex.Store({
  state: {
    msg: 'nyaa',
  },
  mutations: {
    setMessage(state, payload) {
      state.msg = payload
    }
  },
  actions: {
    saveMessage(context, payload) {
      context.commit('setMessage', payload)
    },
  }
})

new Vue({
  el: '#app',
  store,
  render: h => h(App)
})
<template>
  <div>
    <h1>{{ this.$store.state.msg }}</h1>
    <button v-on:click="increaseMsg">click</button>
  </div>
</template>

<script>
export default {
  methods: {
    increaseMsg: function() {
      this.$store.dispatch('saveMessage', this.$store.state.msg+'!')
    }
  }
}
</script>