electron+vue3全家桶+vite项目搭建【16.1】electron多窗口,pinia状态同步,扩展store方法,主动同步pinia的状态【推荐】

文章目录

    • 引入
    • 实现效果如下
    • 实现步骤
      • 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;
  // 初始化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
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
        );
      }
    }
  }
);

你可能感兴趣的:(electron,javascript,前端,pinia,vue3全家桶)