Electron中使用vuex

1,593 阅读3分钟

安装

npm install vuex-electron

使用

使用到了Vuexplugins选项,这个选项暴露出每次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-storekeystorageKey的数据和Vuex的数据进行合并。

后面两条语句就是设置黑名单和白名单,原理就是通过Vuexsubscribe这个钩子函数,接收一个回调方法,该回调会被传入两个参数mutationstate,黑白名单就是通过传入的参数去和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把每个进程的修改都同步到其他的进程,保持状态同步。