文章目录
- 引入
- 实现效果如下
- 实现步骤
- 1.自定义pinia插件
- 2.主进程补充同步处理
引入
demo项目地址
我们之前写了一个自动同步pinia状态的插件,可以参考如下文章
electron+vue3全家桶+vite项目搭建【16】electron多窗口,pinia状态无法同步更新问题解决
这里面有一个较大的弊端,就是pinia中的store,只要其中的某个属性修改,就会触发这个store的全量更新,当我们有一些状态频繁更新的时候,就会影响性能,并且有些窗口中的store其实是不需要同步的,但我们无法进行精准的控制,而且为了保证多个窗口间的同步一致,我们做了很多兜底处理。
现在提供另一个思路,我们不被动的自动更新同步store的状态,而是通过扩展store的actions方法,让业务主动调用方法时来主动通知其他窗口完成同步。
实现效果如下
可以看到,只有当我主动触发同步方法时,才会进行窗口间的状态同步
实现步骤
1.自定义pinia插件
自定义pinia插件,扩展store,扩展一个stateSync方法
-
我们先声明一个stateSync方法,然后在store初始化的时候重写该方法
-
src\store\plugins\shareStoreByActionPlugin.ts
import { ipcRenderer } from "electron";
import cacheUtils from "@/utils/cacheUtils";
import { PiniaPluginContext } from "pinia";// 设置本地store缓存的key
const STORE_CACHE_KEY_PREFIX = "store_";declare module "pinia" {export interface PiniaCustomProperties {// 通知主进程让所有窗口同步pinia的状态stateSync(): void;}
}// 处理electron多窗口,pinia共享问题
export function shareStorePlugin({ store }: PiniaPluginContext) {// 初始化本地缓存版本const storeName: string = store.$id;// 初始化storeinitStore(store);// 重写状态同步方法store.stateSync = () => {updateStoreSync(stringify(store.$state), storeName);};// 监听数据同步修改ipcRenderer.on("pinia-store-set",(event, targetStoreName: string, jsonStr: string) => {console.log("被动更新哦");// 监听到状态改变后,同步更新状态if (storeName === targetStoreName) {// 补充版本号是否重置标识console.log("被动更新状态:" + storeName);const obj = JSON.parse(jsonStr);const keys = Object.keys(obj);const values = Object.values(obj);/// 更新各个key对应的值的状态for (let i = 0; i < keys.length; i++) {changeState(store.$state, keys[i], values[i]);}}});
}/*** 状态更新同步* @param stateJsonStr 序列化的状态修改字符串* @param storeName 修改的状态的名称*/
function updateStoreSync(stateJsonStr: string, storeName: string) {// 通知主线程更新ipcRenderer.invoke("pinia-store-change", storeName, stateJsonStr);// 更新本地缓存的storecacheUtils.set(STORE_CACHE_KEY_PREFIX + storeName, stateJsonStr);
}/*** 修改state的值* 补充 如果反序列化的字段是map类型,需要额外处理*/
function changeState(state: any, key: any, value: any) {if (state[key] instanceof Map) {if (value instanceof Array) {state[key] = new Map(value);} else {state[key] = new Map(Object.entries(value as object));}} else {state[key] = value;}
}/*** 初始化状态对象* @param store*/
function initStore(store: any) {const cacheKey = STORE_CACHE_KEY_PREFIX + store.$id;// 从本地缓存中读取store的值const stateJsonStr = cacheUtils.get(cacheKey);if (stateJsonStr) {const stateCache = JSON.parse(stateJsonStr);const keys = Object.keys(stateCache);const values = Object.values(stateCache);/// 更新各个key对应的值的状态for (let i = 0; i < keys.length; i++) {changeState(store.$state, keys[i], values[i]);}}
}/*** 2023/07/03 自定义序列化方式, 处理ts中map类型/对象序列化后为 {} 的情况*/
function stringify(obj: any): string {return JSON.stringify(cloneToObject(obj));
}// 将字段包含map的对象转为json对象的格式
function cloneToObject(obj: any): any {let newObj: any = obj;if (obj instanceof Map) {return Object.fromEntries(obj);}if (obj instanceof Object) {newObj = {};const keys = Object.keys(obj);const values = Object.values(obj);for (let i = 0; i < keys.length; i++) {const key = keys[i];const value = values[i];newObj[key] = cloneToObject(value);}}if (obj instanceof Array) {newObj = [];for (let i = 0; i < obj.length; i++) {newObj[i] = cloneToObject(obj[i]);}}return newObj;
}
然后我们主动在pinia的初始化中使用插件
- src\store\index.ts
import { createPinia } from "pinia";
// import { shareStorePlugin } from "./plugins/shareStorePlugin";
import { shareStorePlugin } from "./plugins/shareStoreByActionPlugin";const pinia = createPinia();// 添加状态共享插件
pinia.use(shareStorePlugin);export default pinia;
2.主进程补充同步处理
- electron\main\index.ts
- 主进程中添加pinia监听,遍历通知窗口进行pinia的更新
/**pinia多窗口共享 */
ipcMain.handle("pinia-store-change",(event,storeName: string,jsonStr: string,) => {// 遍历window执行for (const currentWin of BrowserWindow.getAllWindows()) {const webContentsId = currentWin.webContents.id;if (webContentsId !== event.sender.id && !currentWin.isDestroyed()) {currentWin.webContents.send("pinia-store-set",storeName,jsonStr);}}}
);