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

文章目录

引入

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

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
        );
      }
    }
  }
);
相关推荐
羲云7 小时前
MOSS-Image 开源后爆火:5B 生图模型真正值得关注的,是可控排版
前端
BreezeJiang7 小时前
闭包和 useState 是两个知识点?你可能只学了半条作用域链
前端·面试
2601_958352907 小时前
一款模块通吃15类语音产品?AN-93如何实现录音笔、门禁、IPC的快速适配
前端·人工智能·嵌入式硬件·回声消除·降噪处理
子兮曰7 小时前
Bun v1.4.4 实测:2,025 个文件,bun fmt 2.6 秒跑完
前端·后端·bun
Liudef067 小时前
用 DeepSeek Search API 做一个干净的单文件搜索页面(附完整源码)
前端·人工智能·工具·搜索·deepseek
忆挽篱笙歌7 小时前
进程替换函数与shenll命令行解释器
前端·javascript·算法
计算机魔术师7 小时前
砸了2200亿美元还是不够用,AWS说2027年也卖完了
前端
2601_965305397 小时前
智慧农用的农业四情分别是什么
服务器·前端·人工智能
老徐聊GEO7 小时前
把“安徽“写进题面之后:多平台AI推荐名单的区域扰动测量(2026)
前端·人工智能·算法·geo优化
小冯不疯8 小时前
688 / 拼多多 / 淘特三类供货链路:从采购单到应付凭证的 4 段对账
javascript