安装
npm install vuex-electron
使用
使用到了Vuex的plugins选项,这个选项暴露出每次mutation之后的钩子,只需要在实例化Vuex时传入vuex-electron暴露的两个plugin就可以实现Vuex的状态在所有进程中同步。
vuex的插件其实就是一个函数,它的第一个参数就是store。
import Vue from "vue"
import Vuex from "vuex"
import { createPersistedState, createSharedMutations } from "vuex-electron"
Vue.use(Vuex)
export default new Vuex.Store({
// ...
plugins: [
createPersistedState(),
createSharedMutations()
],
// ...
})
使用教程中还写道,如果你想createSharedMutations起效,需要在主进程中引入上面的文件,为什么需要这样做呢?后面还有一条重要提醒:如果在渲染进程中需要修改Vuex存的数据,需要使用dispatch或者mapActions,不要使用commit直接去提交修改,不然无法在其他渲染进程中共享。why?
疑问
1、为什么必须要在主进程中注册Vuex?
2、为什么在子进程中不能直接使用commit提交更改?
读一下源码
先读一下createPersistedState(),以下是初始化代码
export default (options = {}) => (store) => {
const persistedState = new PersistedState(options, store)
persistedState.loadOptions()
persistedState.checkStorage() // 验证electron-store实例是否有效
persistedState.loadInitialState() // 合并store和electron-store
persistedState.subscribeOnChanges()
}
可以看到,我们其实可以传一个options进去,首先调用loadOptions方法去加载参数,
loadOptions() {
if (!this.options.storage) this.options.storage = this.createStorage()
if (!this.options.storageKey) this.options.storageKey = STORAGE_KEY
this.whitelist = this.loadFilter(this.options.whitelist, "whitelist")
this.blacklist = this.loadFilter(this.options.blacklist, "blacklist")
}
从这里可以看出来,我们可以传哪些东西了,这里其实使用了electron-store持久化存储,你可以传入一个electron-store的实例和一个key,后面会调用loadInitialState方法将electron-store中key为storageKey的数据和Vuex的数据进行合并。
后面两条语句就是设置黑名单和白名单,原理就是通过Vuex的subscribe这个钩子函数,接收一个回调方法,该回调会被传入两个参数mutation和state,黑白名单就是通过传入的参数去和mutation.type进行比较,判断是否持久化存储。
subscribeOnChanges() {
this.store.subscribe((mutation, state) => {
if (this.blacklist && this.blacklist(mutation)) return
if (this.whitelist && !this.whitelist(mutation)) return
this.setState(state) // 将数据存入electron-store中,持久化存储
})
}
以下是`createSharedMutations`初始化源码:
export default (options = {}) => (store) => {
const sharedMutations = new SharedMutations(options, store)
sharedMutations.loadOptions()
sharedMutations.activatePlugin()
}
先来看一下loadOptions方法:
loadOptions() {
if (!this.options.type) this.options.type = process.type === "renderer" ? "renderer" : "main"
if (!this.options.ipcMain) this.options.ipcMain = ipcMain
if (!this.options.ipcRenderer) this.options.ipcRenderer = ipcRenderer
}
第一行语句会根据当前环境设置type,然后是初始化ipc,
初始化完成后,关键在于activeatePlugin()方法:
activatePlugin() {
switch (this.options.type) {
case "renderer":
this.rendererProcessLogic()
break
case "main":
this.mainProcessLogic()
break
default:
throw new Error(`[Vuex Electron] Type should be "renderer" or "main".`)
}
}
这个方法会根据type走不同的处理逻辑,
rendererProcessLogic() {
// Connect renderer to main process
this.connect()
// Save original Vuex methods
this.store.originalCommit = this.store.commit
this.store.originalDispatch = this.store.dispatch
// Don't use commit in renderer outside of actions
this.store.commit = () => {
throw new Error(`[Vuex Electron] Please, don't use direct commit's, use dispatch instead of this.`)
}
// Forward dispatch to main process
this.store.dispatch = (type, payload) => {
this.notifyMain({ type, payload })
}
// Subscribe on changes from main process and apply them
this.onNotifyRenderers((event, { type, payload }) => {
this.store.originalCommit(type, payload)
})
}
所以这里可以得出第二个疑问的答案,需要使用dispatch才会调用notifyMain通知主进程修改Vuex数据,而且在渲染进程逻辑中并没有看到主进程去监听这个通知,只看到有通知和监听通知事件去修改渲染进程中Vuex状态的代码,
看一下mainProcessLogic代码就知道了,
mainProcessLogic() {
const connections = {}
// Save new connection
this.onConnect((event) => {
const win = event.sender
const winId = win.id
connections[winId] = win
// Remove connection when window is closed
win.on("destroyed", () => {
delete connections[winId]
})
})
// 监听子进程修改vuex状态的事件,子进程修改状态之后,需要通过主进程将状态分发到其他的子进程
this.onNotifyMain((event, { type, payload }) => {
this.store.dispatch(type, payload)
})
// 订阅mutation事件,监听到修改时去通知子进程修改vuex状态
this.store.subscribe((mutation) => {
const { type, payload } = mutation
// 通知子进程修改vuex状态
this.notifyRenderers(connections, { type, payload })
})
}
这就是第一个疑问为什么要在主进程中注册store,不注册的话,就无法将渲染进程vuex状态的修改分发的其他渲染进程。
总结
在Electron中,每个进程中的vuex状态都是相互隔离的,那么在Electron中如何使用Vuex呢,这就是vuex-electron做的事,利用IPC把每个进程的修改都同步到其他的进程,保持状态同步。