如何在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。
期望的数据交换:

graph TD
   A(主窗口数据更新)--数据-->A1(子窗口)
   A--数据-->A2(子窗口2)
   A--数据-->A3(子窗口3)
   
   子窗口数据更新--数据-->B(主窗口)
   B--数据-->子窗口2
   B--数据-->子窗口3

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

曾经的尝试:


#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开头的属性变量名会被标记为需要同步的数据。这样做同样会陷入循环,导致程序崩溃,而且感觉这样做很消耗资源。

阅读 1.3k
1 个回答
新手上路,请多包涵

解决方法:不用vue的监听数据的更新,Pinia有一个$onAction函数,用来监听Store的Action是否被调用


let init: boolean = false;
let syncObj: any = {};
/** 窗口间数据同步 所有数据和主窗口同步 */
export async function syncDataWithMainWindow(context: PiniaPluginContext) {
  if (!init) {
    if (appWindow.label !== 'main') {
      emit('getStore', syncObj);
    } else {
      listen('getStore', () => {
        emit('syncStore', syncObj);
      })
    }
  }
  context.store.$onAction((context2) => {
    if (context2.name === 'sync') return;
    context2.after(() => {
      emit('syncStore', syncObj)
    })
  })
  if (init) return;
  init = !init;
  watch(context.store.syncKeyList.value, () => {
    syncObj = context.store.syncList.value;
  }, { flush: 'post' })
  listen('syncStore', (event: Event<any>) => {
    if (event.windowLabel === appWindow.label) return;
    for (const key in event.payload) {
      if (!Object.prototype.hasOwnProperty.call(event.payload, key)) return;
      const item = event.payload[key];
      context.store.syncList.value[key].value = item._value;
    }
  })
}
推荐问题
logo
Microsoft
子站问答
访问
宣传栏