背景说明
在vue+electron中,一般会使用到Pinia或者Vuex来进行状态管理,由此就会有数据持久化的问题。
拿Pinia举例,在一般web项目中,我们会使用 插件pinia-plugin-persistedstate 来进行数据持久化,但pinia-plugin-persistedstate存储数据的方案是用的localstorage或者sessiostorage来进行数据的存储。
当然在electron中也是可以使用localstorage的,但是会有一定的缺点,比如说localstorage只能在渲染进程中使用,而无法在主进程中使用(当然也可以通过IPC 通信间接操作);应用程序更新或覆盖安装时,localstorage数据可能丢失(原因 Electron 默认将数据存储在用户目录下的临时文件夹中,更新后该目录可能被重置)。
因此,就需要一个具体的方案来解决这个问题。
技术选型:
Pinia + pinia-plugin-persistedstate + electron-store
由于localstorage在electron中时存储在临时文件中,所以可以使用electron-store这个库来在本地存储数据。
解决方案:
在pinia-plugin-persistedstate
的配置中可以自定义storage的存储方式,因此我们可以自定义一个storage来打通electron-store跟Pinia之间的数据同步。
1、安装electron-store库 npm install electron-store
2、在electon的主进程中设置与渲染进程的IPC通信
ipcMain.on('setStore', (_event, key, value) => {
store.set(key, value)
})
ipcMain.on('getStore', (event, key) => {
const result = store.get(key)
event.returnValue = result
})
3、在项目的Pinia的文件夹下创建自定义Storage
注意:自定义Storage要按照文档中的要求,必须含有getItem和setItem两个方法。
const electronStore = {
setItem: (key, value) => {
window.electron.ipcRenderer.send('setStore', key, value)
},
getItem: (key) => {
let value = window.electron.ipcRenderer.sendSync('getStore', key)
return value
}
}
export default electronStore
4、配置到Pinia中即可使用
store.use(
createPersistedState({
storage: electronStore
})
)
至此,vue3+Pinia+electron-store实现本地状态持久化就实现了。
但是我们的思考不能停止,于是我看了一下Pinia关于自定义插件的文档以及plugin-pinia-persistence的源码后,决定自己实现一个plugin-pinia-persistence的功能。
简单实现 plugin-pinia-persistence
从Pinia的文档中有关于插件的相关说明,从文档中可以看到插件是通过 pinia.use()
添加到 pinia 实例的。
我们也可以在插件中添加属性或者方法,一个简单的例子:
import { createPinia } from 'pinia'
// 创建一个插件
const muPlugin = (context) => {
console.log(context) // {store, pinia, app, options}
// 添加属性
context.store.hello = 'world'
}
const pinia = createPinia()
// 将该插件交给 Pinia
pinia.use(muPlugin)
核心思路:
1、状态恢复:插件创建时,拉取本地存储中的数据,同步到pinia的state中。
利用 store.$patch
方法将相关数据设置到state进行状态恢复。
2、同步存储:创建监听,当state的数据发生变化时,将数据同步到本地存储中。
利用 store.$subscribe
监听在state被修改时同步数据到指定存储里。
代码实现:
按照核心思路可以简单实现这个插件,在pinia的文件夹下创建plugin-persistence.js
文件,这里的存储由于是electron项目,所以我继续沿用了electron-store的存储方案。如果是web项目的话,可以根据需求改成localstorage
或sessionstorage
。
import { destr } from 'destr'
import electronStore from './electronStore'
// 数据序列化的方法
const serializer = {
serialize: (data) => JSON.stringify(data),
deserialize: (data) => destr(data) // destr是反序列化的库,优点很多,可以自行查看
}
// 从存储中恢复状态到store
const hydrateStore = (store, context) => {
const fromStorage = electronStore.getItem(store.$id)
if (fromStorage) {
const deserialized = serializer.deserialize(fromStorage)
store.$patch(deserialized)
}
}
// 将store的状态持久化到存储中
const persistState = ({ storeId }, state) => {
const toStorage = serializer.serialize(state)
electronStore.setItem(storeId, toStorage)
}
// 创建持久化的功能
const createPersistedState = (context) => {
const {
pinia,
store,
options: { persist = true }
} = context
// 如果不需要持久化,直接返回
if (!persist) return
// 将数据从存储中恢复状态到store
hydrateStore(store, context)
// 数据变化时,持久化store的状态
store.$subscribe(
(_mutation, state) => {
persistState(_mutation, state)
},
{ detached: true }
)
}
export default createPersistedState
然后在Pinia的入口文件中使用
import { createPinia } from 'pinia'
import createPersistedState from './plugin-persistence'
const store = createPinia()
store.use(createPersistedState)
如果还有更多自定义要求或者要完善插件功能,请看下一篇 保姆级带你手写Pinia插件:plugin-pinia-persistence