Electron+Vue多页面多入口应用,窗口间传输大数据的最优方式sharedWorker

首先看一下窗口间需要传输的最大的数据体,就不全展示了,只看一下概貌,也就是指下文中的this.$store.state:
在这里插入图片描述
Electron在版本8的时候,类似这种的操作是稀松平常的:

var jsonObj = {
    isSendConversation: true,
    isCreatedGroup: true,
    isAddGroupUser: true,
    sendTitle: "添加群成员",
    groupId: groupId,
    activeUsers: groupMemberUserInfos,
    storeState: this.$store.state,
};
ipcRenderer.send("show-ModalComponent-window", {
    url: url,
    source: jsonObj,
    type: "sendUserCard",
    dataKey: currentWindow.id,
});

但自从升级到Electron版本15以后,中间有一些断崖式的升级,ipcRenderer.send携带参数中已经不允许使用非结构化数据了,用以上的方式传递都会报错,因为数据中包含了大量的自定义Object类型,于是乎有人给了这样一个方案:

ipcRenderer.send("show-ModalComponent-window", {
    url: url,
    source: JSON.parse(JSON.stringify(jsonObj)),
    type: "sendUserCard",
    dataKey: currentWindow.id,
});

是的,你没看错,将数据用JSON.parse(JSON.stringify())包一下,这样最初确实是解决了问题,但仅限于对小型固定大小的需要转换的数据,要知道我目前开发的是一个聊天工具,以上数据中存了很多会话种消息,这些都是数量不固定的,伴随数据量增大,于是乎终于有一天爆了,报错:Invalid string length:

在这里插入图片描述

定位错误到这一行:
在这里插入图片描述

于是,就开始了各种的折腾历程:
1、 这些数据一部分是从本地sdk中获取的,但还有一些是处理中生成的,如果在打开的新页面中再从sdk获取,一是速度/性能上不允许,再者也不现实,于是放弃
2、 保存到本地再读取,这个io操作在速度/性能上也是大打折扣的,另外,还需要考虑实时保存变更的数据,也不可取
3、 保存到indexeddb再读取,这个比io操作会快一些,但实时保存变更数据的问题依然是道槛儿,再者,读取操作只能异步处理
4、 数据传到background后,在主进程中设置全局变量:

remote.setGlobal('sharedObject', { someProperty: 'someValue' });

然后,在渲染进程中取出全局变量:

const sharedObject = remote.getGlobal('sharedObject');

但是问题来了,数据传输到主进程岂不是还避免不了JSON.parse(JSON.stringify())的包裹?于是也不靠谱。
不想使用JSON.parse(JSON.stringify())的原因有三,一是有长度限制,再一个数据量越大,转换的耗时也越大,三是最重要的,就是如果里面有object类型,这样的转换会破坏掉此数据格式。
5、幸好,浏览器支持使用shared worker,在不同页面间传递数据,这里说传递,不是共享,因为被传的数据和传递后的数据不是指向同一内存地址的,也就是说两者从此就不再同步了
以下就简单说明一下使用方式:

5.1 首先,在public文件夹中创建一个sharedWorker.js文件,内容如下:

let connectionCount = 0;
let peers = [];

onconnect = function(e) {
  let port = e.ports[0];

  connectionCount++;

  peers.push({
    id: connectionCount,
    port: port
  });

  port.postMessage({
    kind: "connect",
    peerId: connectionCount
  });

  port.onmessage = function (e) {
    if(e.data.kind == "close"){
      const index = peers.findIndex(item => item.id == e.data.peerId);
      if(index >= 0){
        peers.splice(index, 1);
      }
    }else if(e.data.kind == "getCount"){
      peers.forEach((peer) => {
        // 只给请求者发送
        if(peer.id == e.data.peerId){
          peer.port.postMessage({kind: "getCount", count: peers.length});
        }
      });
    }else if(e.data.kind == "getData"){
      // 自己窗口以外的所有窗口广播
      peers.filter(function (peer) {
        return peer.id !== e.data.peerId;
      }).forEach(function (peer) {
        peer.port.postMessage(e.data);
      });
    }else if(e.data.kind == "sendData"){
      // 向自己窗口以外的指定窗口单独发送
      peers.filter(function (peer) {
        return peer.id !== e.data.peerId;
      }).forEach(function (peer) {
        if(!!e.data.toPeerId && peer.id === e.data.toPeerId){
          peer.port.postMessage(e.data);
        }
      });
    }
  };
}

5.2 在store中为各窗口定义相应的传输数据用变量,注意有的窗口需要为数据指定key值,以防使用同一变量,快速点击打开同样的多个窗口(比如类似微信的独立窗口)时造成数据混乱:
在这里插入图片描述

各窗口的入口js中这样定义,我是统一在store中定义的,注意dev和生产环境的路径指定不同:
在这里插入图片描述

再在store中定义此发送/接收用的方法:
在这里插入图片描述
在这里插入图片描述

方法有点儿大,主要关注e.data.kind == “connect”、e.data.kind == “getData”、e.data.kind == "sendData"的相关逻辑即可(e.data.kind == "getCount"是为了观察关闭窗口时有无未关闭的链接,防止造成内存泄露)

5.3 接下来就可以传输数据了,在ipcRenderer.send之前塞入数据:
在这里插入图片描述

经过主进程转发后,在新窗口的渲染进程中通过以上store中的方法发送并接收数据:
在这里插入图片描述

这样,全程避开了JSON.parse(JSON.stringify())的包裹,就可以尽情的在新窗口中使用传入的数据了。

再说一个重点,一般sharedWorker用来多窗口广播的操作比较多(包括广播给自己),但实验发现,如果打开了多个窗口,即时收到广播进行判断不做处理,接收广播的瞬间各窗口的内存也是有消耗的,所以以上代码考虑了指定窗口发送的方式进行传输。

5.4 最后,在窗口关闭时不要忘记关闭port,否则内存会暴涨的哦:
在这里插入图片描述
另外,还有一个前提很重要!!!一定是多页面多入口(每个页面有自己的html文件),才可以使用sharedWorker,否则,逻辑不报错,但事件就是会触发,我就入过此坑,切记!切记!

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的 Electron + Vue.js 的 Demo: 首先,你需要安装 Node.js 和 Vue CLI。然后,创建一个空的 Vue.js 项目: ``` vue create my-electron-app ``` 然后进入项目目录: ``` cd my-electron-app ``` 接着,安装 Electron: ``` npm install electron --save-dev ``` 然后在项目根目录下创建一个 `main.js` 文件,这是 Electron 的主进程代码: ```javascript const { app, BrowserWindow } = require('electron') function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false, enableRemoteModule: true } }) // 加载 Vue.js 应用 win.loadURL('http://localhost:8080') } app.whenReady().then(() => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) ``` 其中,`createWindow()` 函数创建了一个 Electron 窗口,并加载了 `http://localhost:8080` 这个地址,这是后面我们要启动的 Vue.js 应用的地址。 接下来,修改 `package.json` 文件,增加以下内容: ```json { "name": "my-electron-app", "version": "0.1.0", "main": "main.js", "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "electron": "electron ." }, "dependencies": { "vue": "^2.6.11" }, "devDependencies": { "electron": "^11.1.1", "vue-cli-plugin-electron-builder": "^2.0.0-rc.3" }, "electronBuilder": { "nodeIntegration": true, "builderOptions": { "appId": "com.example.my-electron-app", "productName": "My Electron App", "win": { "target": "nsis" } } } } ``` 其中,`electron` 命令启动了 Electron,`vue-cli-plugin-electron-builder` 是一个 Vue.js 插件,可以帮助我们打包 Electron 应用。 最后,运行以下命令启动 Vue.js 应用: ``` npm run serve ``` 然后在另外一个终端窗口中,运行以下命令启动 Electron: ``` npm run electron ``` 这样,一个简单的 Electron + Vue.js 的应用就创建好了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值