当前位置: 首页 > 知识库问答 >
问题:

javascript - 如何在Tauri+Pinia项目中实现窗口间状态数据同步?

左丘宜年
2024-08-13

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个答案

邴姚石
2024-08-13

解决方法:不用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;
    }
  })
}
 类似资料:
  • vue3+pinia+piniaPluginPersistedstate持久化sessionStorage缓存,页面中某个按钮点击单独open一个浏览器新窗口,执行某些操作后,更新pinia数据,更新后主窗口和新窗口的pinia数据不同步嘛? 源窗口id已更新,之前打开的新窗口还是显示的新窗口打开时的id 源窗口更新后的id值: 新窗口还是显示的新窗口打开时的id值: 如果不同步的话,请问有啥解决

  • 我正在构建一个具有以下目标的 Flink 应用程序: 将事件收集到键控的非活动触发会话窗口中 尽早发出输入事件的副本,并通过会话引用进行增强 打开和关闭会话时发出会话更新以及收集的会话统计信息(在会话关闭时) 通过滚动窗口,我已经能够实现上述目标,但在会话窗口中我没有成功。 我的窗口处理代码如下 来生成我正在使用的输入 和 当我使用会话窗口执行时,会出现以下异常: 希望我错过了一个技巧,因为无法使

  • 本文向大家介绍如何在JavaScript中实现多态?,包括了如何在JavaScript中实现多态?的使用技巧和注意事项,需要的朋友参考一下 多态性 多态 是面向对象编程(OOP)的宗旨之一。它有助于设计对象,使其可以与特定提供的对象共享或覆盖任何行为。多态性 利用继承的 优势来实现这一点。 在以下示例中,子对象(例如“板球”和“网球”)已覆盖从父对象“游戏”调用的“选择”方法,并分别返回了新字符串

  • 请把这个问题当作严格的教育问题来处理。我仍然有兴趣听到新的答案和想法来实现这一点 如何用JavaScript实现双向数据绑定? 通过与DOM的数据绑定,我的意思是,例如,拥有一个带有属性的JavaScript对象。然后拥有一个DOM元素(例如),当DOM元素发生变化时,也会发生变化,反之亦然(也就是说,我指的是双向数据绑定)。 在简单的JavaScript中实现这一点的一些基本技术是什么? 具体来

  • 我开始着手一个JavaFX项目,在这个项目中,我想添加一个复制的文本,而不必更改驱动程序中的格式和图像(它应该像open office一样工作)。图像应该能够放置在文本中不同的位置。因此,我需要一个可以处理rtf格式的控制字段。 为此,我找到了RichTextFX。文档中解释了如何使用Maven或Grandle运行RichTextFX。我不使用Maven或Grandle,因此我想知道是否有可能在不

  • 如何在thymeleaf超文本标记语言文档中使用户data-Target="#userId"。例如,在任何JSP或任何超文本标记语言中, 我如何在thymeleaf HTML文档中编写。这样地? 例外情况是:

  • 我对流中的事件进行了键控,我希望通过键来累积,直到超时(例如,5分钟),然后处理累积到该点的事件(忽略该键之后的所有内容,但首先是第一件事)。 我是一个新的Flink,但从概念上来说,我认为我需要一些类似下面代码的东西。 如何在Flink中完成键控窗口超时?

  • 我可否问一问作出决定的理由是什么?我是否可以推断,如果数据的到达非常不规则(50%进入定义的窗口长度,而其他50%没有),窗口方法的结果更有偏差(因为50%的事件被丢弃)? 另一方面,在使用状态时,我们是否花费更多的时间检查和更新状态?