文章目录
引入
我们之前写了一个自动同步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
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;
// 初始化store
initStore(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);
// 更新本地缓存的store
cacheUtils.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
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的更新
ts
/**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
);
}
}
}
);