这个问题是解决基于 vue 和 electron 的开发中使用 vuex 的 dispatch 无效的问题,即解决了 Please, don't use direct commit's, use dispatch instead of this.
问题。
先允许我梳理一下目录结构,以便阅读的时候不会一头雾水,你到底说的这个文件是哪个……
其中 /src/main
是存放主配置文件的,/src/render
下面有 store
、router
、components
等。
components
下面就是很多 .vue
文件,router
下面就是一些路由配置的 js
文件和一些拦截器的 js
。
关键是 store
,store
下面有一个 index.js
的主配置文件 index.js
,和一个 modules
文件夹。
index.js
里面写的是(记住这句话,后面会用到):
import Vue from 'vue'
import Vuex from 'vuex'
import { createPersistedState, createSharedMutations } from 'vuex-electron'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
plugins: [
createPersistedState(),
createSharedMutations()
],
strict: process.env.NODE_ENV !== 'production'
})
而 modules/
下面存放各个实体,例如上图中的 Auth.js
和 Counter.js
,并通过 index.js
全部引入。
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('.', false, /.js$/)
const modules = {}
files.keys().forEach(key => {
if (key === './index.js') return
modules[key.replace(/(./|.js)/g, '')] = files(key).default
})
export default modules
然后来看一个 vuex 的官方样例:
const state = {
main: 0
}
const mutations = {
DECREMENT_MAIN_COUNTER (state) {
state.main--
},
INCREMENT_MAIN_COUNTER (state) {
state.main++
}
}
const actions = {
someAsyncTask ({ commit }) {
// do something async
commit('INCREMENT_MAIN_COUNTER')
}
}
export default {
state,
mutations,
actions
}
之后很显然的,我想要在 Vue 的组件调用 INCREMENT_MAIN_COUNTER
对计数器加 1。
this.$store.commit('INCREMENT_MAIN_COUNTER');
// this.$store.commit('INCREMENT_MAIN_COUNTER', payload);
如果是一般的 vue,就 OK 了,但是,我遇到了报错,说,Please, don't use direct commit's, use dispatch instead of this.
那好吧,没事,不就是不然用 Commit,非要用 Dispatch 嘛,那我就写一个 Action,里面直接调用 Mutation,就像这个样子:
const actions = {
JUST_INCREASE ({ commit }) {
commit('INCREMENT_MAIN_COUNTER')
}
}
然而奇怪的事情是,this.$store.dispatch('JUST_INCREASE')
并不能运行,没反应,计数器还是 0,不能赋值,就像是这个函数没有被执行一样。没有报错,没有任何异常,查也查不出什么问题。
网上的资料似乎也挺少。
折腾了很久,后来发现是 vuex-electron 里面一个插件的锅。
解决方法有两个。
方法一:
在 store/index.js
里面,就是上文特别强调了的那个文件,去掉 createSharedMutations
插件。
import Vue from 'vue'
import Vuex from 'vuex'
import { createPersistedState, createSharedMutations } from 'vuex-electron'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
plugins: [
createPersistedState(),
createSharedMutations() // 注释掉这一行
],
strict: process.env.NODE_ENV !== 'production'
})
这是因为 vuex-electron
引入了一个用于多进程间共享 Vuex Store 的状态的插件。如果没有多进程交互的需求,完全可以不引入这个插件。
注释掉以后重启项目,用 this.$store.commit('XXX')
就可以使用了。
然而,如果需要多进程来处理怎么办?
方法二:
https://github.com/vue-electron/vuex-electron#installation
看第 3 条:
In case if you enabledcreateSharedMutations()
plugin you need to create an instance of store in the main process. To do it just add this line into your main process (for examplesrc/main.js
):
import './path/to/your/store'
这种时候就不能用第一种方法来解决问题了。
好在文档也说了,加上一行导入。
找到 /src/main/index.js
,在前面加上一句:
import '../renderer/store'
之后一切正常,可以使用 Dispatch 来进行操作了。
最后还有一个比较奇怪的问题:
在直接调用 state
的时候,这样写 this.$store.state.loginStatus
是不行的,会 undefined,必须写成 this.$store.state.Auth.loginStatus
,就像是 this.$store.state.Counter.main
一样,似乎可以解释为,不同的模块不指定名字的话就找不到。
但是,在写 Dispatch 的时候又不需要指定名字了,直接 dispatch('changeLoginStatus')
就行了,不然难道不应该是也按照 dispatch('Auth/changeLoginStatus')
这样子来写嘛……