electron 开发经验之谈系列-渲染、主进程通信
本文最后更新于:2021年4月8日 下午
场景
由于 electron 应用分为主进程、渲染进程,所以它们之间需要通信。而 electron 本身实现了一个简单的 event emitter 通信模型,虽然能用,但并不足够好。
问题
- 基于字符串和约定进行通信本质上和当下前后端通信差不多,没有利用同构优势
- 使用起来没有任何限制,意味着很难维护(非强制性的约定基本上都很难生效)
思考
那么一共 electron 进程通信有哪些情况呢?
- 渲染进程 => 主进程
- 主进程 => 渲染进程
- 渲染进程 => 渲染进程
而其中最常用的便是 渲染进程 => 主进程
其实吾辈也看过许多 electron 进程通信的 封装库 或者类似场景的 rpc 实现 comlink,但最终还是决定使用接口 + 主进程实现 + 渲染层根据接口生成 Client 的方式实现。
最终,吾辈选择了接口 + 实现类的基本模式
实现渲染进程 => 主进程
首先在创建 libs 目录用以存放通用模块(非业务),然后创建三个模块
electron_ipc_type
: 一些需要引入的类型electron_ipc_main
: 主进程封装electron_ipc_renderer
: 渲染层封装
此处使用 rollup 进行打包
大致实现
electron_ipc_type: 通用的基本接口定义,必须包含一个 namespace
属性
1 |
|
electron_ipc_main: 封装主进程实现相关代码,主要保证类型安全
1 |
|
electron_ipc_renderer: 渲染进程
1 |
|
使用
在 apps 下创建一个模块 shared_type
,里面包含一些渲染进程与主进程之间共享的类型,下面是一个简单的示例
1 |
|
在主进程中使用 class 实现它并注册
1 |
|
在渲染进程中创建客户端对象并使用
1 |
|
实现主进程 => 渲染进程
由于吾辈的 ui 层框架使用了 react,所以基于 class 的模式在此并不适用,需要使用某种变通的方式(吾辈仍然不愿意放弃将 class 作为命名空间的想法)。
1 |
|
1 |
|
使用
在渲染进程使用 hooks 注册它
1 |
|
在主进程生成客户端实例调用它
1 |
|
约定俗成
- 在
shared_type
模块中的接口定义总是*Define
形式,且实现的BaseDefine<T>
泛型参数是*Api
形式 - 在
main
模块中实现的 class 总是*Api
形式 - 在
renderer
模块中获取的 client 实例总是*Api
小写驼峰形式 - 实现
BaseDefine<T>
传入的命名空间参数不应该重复
总结
electron 本身的进程通信 api 在逐渐发展,但目前仍然没有足够好用,所以吾辈不得不进行了封装。