xJuunn 2024-08-11 17:18 采纳率: 66.7%
浏览 4

Tauri+Pinia 实现窗口间数据同步

Tauri+Pinia 实现窗口间数据同步

我是一名初学者,我在使用Tauri+vue3+pinia开发桌面应用的时候,发现窗口间的Pinia对象是独立的,但是我现在的需求是,Pinia中的部分数据是可以窗口间同步的(比如:有一个count属性,当其中一个窗口改变时,其他窗口同步改变)。
这是我目前的实现方法

import { createPinia } from 'pinia';
import { emit, listen, type Event } from '@tauri-apps/api/event';
import { appWindow } from '@tauri-apps/api/window';
export default defineNuxtPlugin((nuxtApp: any) => {
  const pinia = createPinia();
  nuxtApp.vueApp.use(pinia)
  pinia.use(syncDataWithMainWindow);
})

/** 窗口间数据同步 所有数据和主窗口同步 */
export async function syncDataWithMainWindow(context: any) {
  watch(context.store, () => {
    if (!context.store.watchEnabled.value) {
      context.store.setWatchEnabled(true)
      return;
    }
    emit('syncStore', context.store);
  }, { flush: 'post' });
  listen('syncStore', (event: Event<any>) => {
    if (context.store.label.value === event.payload.label._value) return;  // 忽视自己发送的事件
    // 非主窗口停止监听转发数据,只有主窗口接收到数据会进行转发
    if (appWindow.label !== 'main') {
      context.store.setWatchEnabled(false);
    }
    context.store.window_sync_source.value = event.payload.label._value;   // 记录数据来源
    context.store.syncCount.value = event.payload.syncCount._value
  })
}

目前存在的问题是,在代码同步完成后,会触发watch,然后陷入同步的循环,虽然我做了watch开关处理,但是窗口间的数据同步本身就是非同步的过程,这样做依然会陷入循环,而且这样做感觉埋了一个惊天大bug。
期望的数据交换:

img

大家有没有更好的方法实现😭😭

曾经的尝试:


#region 所有窗口之间相互同步,资源消耗大
export async function syncDataBetweenWindows(context: any) {
  await initSync(context);
  updateSync(context);

}
function initSync(context: any) {
  return new Promise((res, rej) => {
    let timeout = setTimeout(() => {
      rej(false);
      throw new Error("页面初始化获取全局Store超时");
    }, 3000);
    once('initStoreSync', (event: any) => {
      sync(event, context);
      clearTimeout(timeout);
      res(true);
    })
    listen('initStore', (event: any) => {
      if (event.payload.label === appWindow.label) return;
      emit('initStoreSync', {
        data: {
          store: context.store,
          label: appWindow.label
        }
      })
    })
    emit('initStore', { label: appWindow.label });
  })
}
function updateSync(context: any) {
  watch(context.store, () => {
    if (context.listenUpdate) {
      context.listenUpdate = false;
      return;
    }
    emit('storeSync', {
      data: {
        store: context.store,
        label: appWindow.label
      }
    })
  })

  listen('storeSync', async (event: any) => {
    sync(event, context);
  })
}
function sync(event: any, context: any) {
  if (event.payload.data.label == appWindow.label) return;
  for (const key in event.payload.data.store) {
    if (!Object.prototype.hasOwnProperty.call(event.payload.data.store, key)) continue;
    if (key.substring(0, 4) != 'sync') continue;
    context.listenUpdate = true;
    context.store[key].value = event.payload.data.store[key]._value;
    console.log('数据同步');

  }
}
#endregion

所有窗口之间的数据同步,在store中,使用sync开头的属性变量名会被标记为需要同步的数据。这样做同样会陷入循环,导致程序崩溃,而且感觉这样做很消耗资源。

  • 写回答

2条回答 默认 最新

  • 一直免费一直爽 2024-08-11 17:18
    关注

    以下回复参考:皆我百晓生券券喵儿等免费微信小程序作答:

    在您的项目中,您正在尝试将PiniaVue结合,并使用它来实现一个功能,其中一些状态可以在不同窗口(例如main窗口)之间共享。为了实现这个需求,您可以考虑以下几种策略:

    1. 使用 Piniastatecomputed 属性

    首先,您可以尝试将所有需要同步的状态都放在Piniastatecomputed中。这样做的好处是在不同的窗口中都可以访问这些状态,同时不会引入额外的同步开销。每个窗口可以通过观察其标签来决定是否要同步状态。

    // index.ts
    export interface StoreState {
      main: {
        count: number;
        syncCount?: number;
      };
      subWindow1: {
        count: number;
      };
      subWindow2: {
        count: number;
      };
    }
    
    export const useStore = defineStore({
      id: 'store',
      state: () => ({
        main: {
          count: 0,
        },
        subWindow1: {
          count: 0,
        },
        subWindow2: {
          count: 0,
        },
      }),
      getters: {},
    });
    
    // 在需要的地方调用使用Store的方法
    useStore().setMain({ count: 5 });
    useStore().getSubWindow1().count;
    

    2. 使用 Piniasync 方法

    另一个选择是通过使用Piniasync方法来实现窗口间的状态同步。这种方法的优点是更简单,不需要修改现有的业务逻辑,只需要在需要同步的地方添加sync函数。

    // index.ts
    export interface StoreState {
      main: {
        count: number;
        syncCount?: number;
      };
      subWindow1: {
        count: number;
      };
      subWindow2: {
        count: number;
      };
    
      syncWithSubWindow1: boolean;
      syncWithSubWindow2: boolean;
    
      syncCount: number;
    }
    
    export const useStore = defineStore({
      id: 'store',
      state: () => ({
        main: {
          count: 0,
        },
        subWindow1: {
          count: 0,
        },
        subWindow2: {
          count: 0,
        },
        syncWithSubWindow1: false,
        syncWithSubWindow2: false,
    
        syncCount: 0,
      }),
      actions: {
        setMain(state: StoreState['main']) {
          this.main.count = state.count;
          this.syncWithSubWindow1 = true;
        },
    
        getSubWindow1(state: StoreState['subWindow1']) {
          this.subWindow1.count = state.count;
          this.syncWithSubWindow2 = true;
        },
    
        setSubWindow2(state: StoreState['subWindow2']) {
          this.subWindow2.count = state.count;
          this.syncWithSubWindow1 = true;
          this.syncWithSubWindow2 = true;
        },
      },
      getters: {
        getMain() {
          return this.main;
        },
        getSubWindow1() {
          return this.subWindow1;
        },
        getSubWindow2() {
          return this.subWindow2;
        },
        getCount() {
          return this.main.count + this.subWindow1.count + this.subWindow2.count;
        },
      },
    });
    
    // 在需要的地方调用使用Store的方法
    useStore().setMain({ count: 5 });
    useStore().getSubWindow1().count;
    useStore().getSubWindow2().count;
    

    3. 使用第三方库

    另一种选择是考虑使用第三方库,如@tauri-apps/api/window,它提供了window.sync接口,可以用于异步地同步Pinia的状态。

    // index.ts
    import { window } from '@tauri-apps/api/window';
    
    export interface StoreState {
      main: {
        count: number;
        syncCount?: number;
      };
      subWindow1: {
        count: number;
      };
      subWindow2: {
        count: number;
      };
    
      syncWithSubWindow1: boolean;
      syncWithSubWindow2: boolean;
    
      syncCount: number;
    }
    
    export const useStore = defineStore({
      id: 'store',
      state: () => ({
        main: {
          count: 0,
        },
        subWindow1: {
          count: 0,
        },
        subWindow2: {
          count: 0,
        },
        syncWithSubWindow1: false,
        syncWithSubWindow2: false,
    
        syncCount: 0,
      }),
      actions: {
        setMain(state: StoreState['main']) {
          this.main.count = state.count;
          this.syncWithSubWindow1 = true;
        },
    
        getSubWindow1(state: StoreState['subWindow1']) {
          this.subWindow1.count = state.count;
          this.syncWithSubWindow2 = true;
        },
    
        setSubWindow2(state: StoreState['subWindow2']) {
          this.subWindow2.count = state.count;
          this.syncWithSubWindow1 = true;
          this.syncWithSubWindow2 = true;
        },
      },
      getters: {
        getMain() {
          return this.main;
        },
        getSubWindow1() {
          return this.subWindow1;
        },
        getSubWindow2() {
          return this.subWindow2;
        },
        getCount() {
          return this.main.count + this.subWindow1.count + this.subWindow2.count;
        },
      },
    });
    
    // 同上
    useStore().setMain({ count: 5 });
    useStore().getSubWindow1().count;
    useStore().getSubWindow2().count;
    

    注意事项

    • 性能:使用Piniasync方法可能会对性能造成一定的影响,特别是对于大量数据的同步。
    • 复杂性:使用第三方库可能使代码更加复杂,尤其是在处理多窗口场景时。
    • 兼容性:确保所有的Pinia组件都正确地使用了新的API,以避免潜在的问题。

    希望这些建议能帮到您!如果您有任何疑问,欢迎提问。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 8月11日

悬赏问题

  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见
  • ¥15 一共有五道问题关于整数幂的运算还有房间号码 还有网络密码的解答?(语言-python)
  • ¥20 sentry如何捕获上传Android ndk 崩溃
  • ¥15 在做logistic回归模型限制性立方条图时候,不能出完整图的困难
  • ¥15 G0系列单片机HAL库中景园gc9307液晶驱动芯片无法使用硬件SPI+DMA驱动,如何解决?