vue3+pinia+piniaPluginPersistedstate持久化sessionStorage缓存,页面中open一个新窗口,执行操作后更新pinia,两个窗口的数据不同步嘛?

vue3+pinia+piniaPluginPersistedstate持久化sessionStorage缓存,页面中某个按钮点击单独open一个浏览器新窗口,执行某些操作后,更新pinia数据,更新后主窗口和新窗口的pinia数据不同步嘛?
image.png
image.png
源窗口id已更新,之前打开的新窗口还是显示的新窗口打开时的id
源窗口更新后的id值:
image.png
新窗口还是显示的新窗口打开时的id值:
image.png
如果不同步的话,请问有啥解决办法嘛?

阅读 6.8k
4 个回答

不会同步。因为 sessionStorage 并不是响应式的,并且新窗口的 pinia 是一个新的实例,数据更新后不同的 pinia 实例之间并不能同步。若两个窗口之间需要通信,可通过如下示例实现:

// 源窗口发送数据
dispatchEvent(new CustomEvent("hello", { detail: "我是一些数据" }));

// 新窗口接收数据
window.opener.addEventListener("hello", (event) => {
  console.log("收到数据", e.detail);
});


// 新窗口发送数据
window.opener.dispatchEvent(new CustomEvent("hi", { detail: "我是一些数据" }));

// 源窗口接收数据
window.addEventListener("hi", (event) => {
  console.log("收到数据", e.detail);
});

或者将 dispatchEvent 改为winodw.postMessage亦可。

需要注意的是,新窗口不能刷新,否则可能会导致无法通信。

在pinia-plugin-persistedstate插件之上在扩展广播通道,不知道有没有bug

import { useBroadcastChannel } from '@vueuse/core';
import { watch } from 'vue';

export function createSyncPlugin() {
  return ({ store }) => {
    const { data, post } = useBroadcastChannel({ name: `pinia-sync-${store.$id}` });

    // 监听来自其他窗口的消息并更新 store
    watch(data, (newValue) => {
      if (newValue) {
        const parsedData = JSON.parse(newValue as string);
        store.$patch(parsedData);
      }
    });

    // 监听本窗口内的 store 变化并广播消息
    store.$subscribe((_mutation, state) => {
      const deserializeData = JSON.stringify(state);
      post(deserializeData);
    });
  };
}
import { createPinia } from 'pinia';
import { createPersistedState } from 'pinia-plugin-persistedstate';

import { createSyncPlugin } from './plugins/pinia-plugin-sync';

const store = createPinia();
store.use(createPersistedState());
store.use(createSyncPlugin());

export { store };

export * from './modules/xxxx1';

export default store;
import { defineStore } from 'pinia';
export const useSettingStore = defineStore('setting', {
  state: () => {
    theme: 'dark'
  },
  getters: {},
  actions: {},
  persist: true, // 数据持久化
});
1. main.js中:window.vueGlobalProperties = app.config.vueGlobalProperties;
2. 子窗口中定义,将父窗口的pinia赋值给子窗口:
const parentPinia = window.opener.vueGlobalProperties.$pinia;
3. 可以通过监听父窗口store状态发生变化时,给子窗口发送事件的方式,实现同步更新。
// 新打开的子窗口
let NewWin = window.open(url); 
// 父窗口监听pinia数据有变化时,更新子窗口
store.$subscribe(() => {
    if (NewWin) {
        NewWin.postMessage("updateStore")
    }
})
// 子窗口接收更新事件
window.onMessage = event => {
    if (event.data === "updateStore") {
         store.$patch(parentPinia.state.value[k])
    }
}

image.png
刚好也遇到了同源多页签/窗口之间共享数据的需求,找到了这个Broad Cast Channel 通信API。我先立个Flag,等有生之年我发布了这个 pinia 插件发布了再来,补充。估计最终实现逻辑与上面两位答主的思路不会有区别,都是发布订阅

2024年5月28日

找到了一个已经实现的插件,看了源码思路相同,甚至同步实现了 Storage的同步 pinia-persist-share

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏