[vuex] 新开标签页无法读取到原有state数据问题

167 阅读1分钟

问题:

通过window.open()新开页面,新页面无法读取到原有的state数据。

解决过程

查阅文档后发现,vuex存储的state数据无法在多页面中进行共享,也就是说新开标签页是无法读取到原页面的state数据的。貌似是因为vuex的故意设计如此,专门为了单页面应用共享数据使用;新开页面的时候会创建一个新的store实例,和原有页面非一个实例。

解决方案

新开页面时通过localStorage浏览器缓存传递数据

代码

// ---原页面,传递数据-------------------------------------------

// vue-router带参打开新页面
const href = this.$router.resolve({
  path: '/bill-detail',
  query: { ... },
}).href;
const newWindow =  window.open(href, '_blank');

// 之所以写这么麻烦,是因为本司系统中部分模块state数据过大,超过了localStorage数据大小限额
// 所以分开模块进行传递

// 所有被缓存的state模块的key
const store_storage_keys = [];
for (const store_item_key in this.$store.state) {
  // 捕获报错,防止某store模块state出现问题影响其他模块
  try {
    // 根据模块key生产跳转前临时存储缓存的key
    const key = "STORE_STATE_" + store_item_key.toUpperCase();
    // 分模块存储state数据至缓存中
    localStorage.setItem(key, JSON.stringify(this.$store.state[store_item_key]));
    // 成功存储后将本模块的storage.key存入数据key列表,方便接收页进行获取
    store_storage_keys.push(key);
  } catch (e) {
    // 单模块数据缓存抛错
    console.error(store_item_key + "超过数据大小限制,无法传输");
  }
}
// 成功存储后将本模块的storage.key存入数据key列表,方便接收页进行获取
localStorage.setItem("STATE_STORAGE_KEYS", store_storage_keys.toString());
// 新标签页打开后发送单据数据
newBillWindow.onload  = () => {
  newBillWindow.postMessage({
    key: 'newWindowKey',
    type: 'newWindowKey',
    data: { ... },
  }, window.location.origin)
}


// ---新页面,接收数据-------------------------------------------

// 本页面是新开页面,需要监听接收父源页面给出的参数
window.addEventListener('message', this.linstenerParentMessage);
// 获取需要接收的所有store.state模块key
const stateKeys = localStorage.getItem("STATE_STORAGE_KEYS")?.split(',');
// 存储获取到的state值
const stateObj = {};
for (let i = 0; i < stateKeys?.length; i++) {
    // 根据获取到的state模块storageItem.key还原获取到原本store.state模块key
    const originKey = stateKeys[i].replace('STORE_STATE_', '').toLowerCase();
    // 获取store每个模块的state数据
    const stateItemData = JSON.parse(localStorage.getItem(stateKeys[i]) || {});
    // 存入map
    Reflect.set(stateObj, originKey, stateItemData);
}
// 保存至当前标签页的store实例数据
this.$store.replaceState(Object.assign({}, this.$store.state, stateObj));