Electron+Vue3数据持久化:基于Pinia与electron-store的跨进程存储方案

567 阅读3分钟

背景说明

在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之间的数据同步。 image.png

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进行状态恢复。

image.png

2、同步存储:创建监听,当state的数据发生变化时,将数据同步到本地存储中。
利用 store.$subscribe 监听在state被修改时同步数据到指定存储里。

image.png

代码实现:

按照核心思路可以简单实现这个插件,在pinia的文件夹下创建plugin-persistence.js文件,这里的存储由于是electron项目,所以我继续沿用了electron-store的存储方案。如果是web项目的话,可以根据需求改成localstoragesessionstorage

    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