electron 使用ipcRenderer,ipcMain 实现进程之间的通信

官方使用的通信方式

  • 官方文档的通信 也是使用ipcMain,ipcRenderer两个模块来做的通信,这两个模块是核心。
  • 但是要实现真正的通信需要配合预加载脚本来实现。
  1. 第一步:创建一个窗口时,必须要写入以下配置,预加载脚本。
// main.js
const mainWindow = new BrowserWindow({
   webPreferences: {
     preload: path.join(__dirname, 'preload.js')
   }
 })
  1. 第二步:contextBridge.exposeInMainWorld 方法意思为,暴露一个 ‘electronAPI’ ,的对象在window上,可以使用window.electronAPI 来获取这个对象
// preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
	// ipcRenderer.send向主进程 发送消息
  setTitle: (title) => ipcRenderer.send('set-title', title)
})
  1. 第三步:调用 第二步暴露出来的对象和方法
// index.html 
div.addEventListener('click', () => {
  window.electronAPI.setTitle('我是ajin')
})
  • 这是官方提供的通信方式,以上通信是渲染进程向主进程通信。想要了解更多官方提供的方式请移步到官方文档 进程之间的通信

preload.js 的代替之法

使用preload.js 的 缺点:

  • 维护的心智负担比较大,
  • 对于没有接触的人来说 不易于理解
  • 写着不舒服,没有遵循内聚准则

解决办法:

  • preload.js 相当于中间绑定方法,通过electron 提供的方法把事件绑定到window上。
  • 我们可以实现一个类,把proload 做的事情,交给自定义类来做,比如保存 交互方法 、 ipc 对象,app 对象等。
  • 后期统一维护这个静态类,所有交互逻辑,事件传参,都通过这个类中转

下面我们通过一个例子来实现,效果如下

  1. 主进程 上点击关闭应用程序的按钮
  2. 渲染进程弹出一个自定义modal,询问是否要关闭应用
  3. 渲染进程点击确定,调用主进程的关闭程序方法

在这里插入图片描述

一、主进程 向 渲染进程通信

思路:

  • 主进程 向 渲染进程 通信需要 调用 window.webContents.send(key, value) 方法。
  • 渲染进程 通过 ipcRenderer.on(channel, listener) 监听 主进程 的调用
  1. main.js 主进程初始化的时候,把app, BrowserWindow 绑定到类上去
// ipc-api/ipcApi.ts
class IpcApi {
  /** BrowserView 创建出来 */
  static window : Electron.BrowserView
  static app : Electron.App
  /** 主进程初始方法 */
  static installMain (ipcMain, win, app) {
    IpcApi.window = win
    IpcApi.app = app
  }
}
export default IpcApi
  • main.js 调用 IpcApi.installMain,传入参数
import { IpcApi } from '../ipc-api'
import { app, BrowserWindow from 'electron'

const win = new BrowserWindow({ ... })
// ... 省略的其他代码
IpcApi.installMain(win, app)
// ....省略的其他代码

  1. 定义主进程向 渲染进程发送消息的 方法emit
class IpcApi {
  /** BrowserView 创建出来 */
  static window : Electron.BrowserView
  static app : Electron.App
  /** 主进程初始方法 */
  static installMain (ipcMain, win, app) {
    IpcApi.window = win
    IpcApi.app = app
  }
  /** 向主进程发送消息 */
  static emit(key: string, value: any) {
    IpcApi.window.webContents.send(key, value)
  }
}
export default IpcApi
  1. 在main.js 中 调用 emit 方法,发送消息
const win = new BrowserWindow({ ... })
win.on('close', (e) => {
 e.preventDefault()
  IpcApi.emit('onClose', '点击了关闭按钮')
})
  1. 渲染进程 监听主进程里面的事件调用。得到事件响应
  • 使用import 引入 ipcRenderer 通信对象
  • 调用 ipcRenderer.on(channel, listener) 监听 channel, 当新消息到达,将通过 listener(event, args…) 调用 listener。
  • 使用react 开发,可以把下面代码封装成一个hooks 进行调用
// App.tsx
(async () => {
if (PLATFORM === 'electron') {
     const { ipcRenderer } = await import('electron')
     ipcRenderer.on('onClose' , (_, e) => {
       console.log(e,'---->')
     })
   }
 })()

在这里插入图片描述

主进程 向 渲染进程的 单向通信就完成了,重点就是import 的引入 const { ipcRenderer } = await import(‘electron’)

渲染进程 向 主进程通信

思路:

  • 注册渲染进程需要调用 主进程 的事件
  • 把注册好的事件暴露到全局(window),方便调用
  • window.ipcApi.closeWindow() 调用 方法
  1. 绑定 我们要通信 的 事件,新增 handlersEvent 方法,此方法一定要在installMain 之前。把我们定义好的交互方法放入类里面,后续好进行注册等操作。
type  Events = {name: string, fn: Function}[]
class IpcApi {
  static events: Events = []
  // ... 省略的其他代码
  /** 绑定所有的调用 方法,该方法调用,需要在 installMain 之前 */
  static handlersEvent(events: Events) {
    IpcApi.events = [...events]
  }
}
export default IpcApi
  • 调用 IpcApi.handlersEvent,定义所有的交互方法。注释的就是关闭窗口方法
// ipc-api/index.ts
import IpcApi from './ipcApi'
IpcApi.handlersEvent([
  { 
  	name: 'closeWindow', fn: () => { 
  	 console.log('---点击了modal 框的确认,调用下面的方法,关闭应用')
  	 // IpcApi.window = null
	 // IpcApi.app.exit()
  	} 
  },
])
export { IpcApi }
  • 到这一步,我们需要修改一下installMain 方法,使用 ipcMain.on 订阅所有的事件。ipcMain.on文档
static installMain (ipcMain: Electron.IpcMain, win: Electron.BrowserView , app: Electron.App) {
  /** 把所有的事件,绑定到 ipcMain 上 */
  IpcApi.events.forEach(({name, fn}) => {
    ipcMain.on(name, () => fn())
  })
}
  • installRenderer 绑定 send 事件 icpRenderer.send ,调用send,向主进程发送消息。 icpRenderer.send,并把eventsMap 对象返回
static installRenderer(icpRenderer: Electron.IpcRenderer) {
  const eventsMap = {}
  for (const {name} of IpcApi.events) {
    eventsMap[name] = (value) => {
      icpRenderer.send(name)
    }
  }
  return eventsMap
}
  • 调用 installRenderer,把返回的 eventsMap 挂载到window
// App.tsx
(async () => {
  // 是否是electron 环境
  if (PLATFORM === 'electron') {
    const { ipcRenderer } = await import('electron')
    const { IpcApi } = await import('../ipc-api')
 	// 把方法挂在window上,你也可以挂载在其他的地方
    window.ipcApi = IpcApi.installRenderer(ipcRenderer)
  }
})()
  • App.jsx 完整代码
const App = () => {
	// installRenderer,
	useEffect(() => {
	  (async () => {
	    if (PLATFORM === 'electron') {
	      const { ipcRenderer } = await import('electron')
	      const { IpcApi } = await import('../ipc-api')
	      window.ipcApi = IpcApi.installRenderer(ipcRenderer)
	    }
	  })()
	}, [])
	
	// 监听,主进程调用的方法
	useEffect(() => {
		(async () => {
	      if (PLATFORM === 'electron') {
	        const { ipcRenderer } = await import('electron')
	        ipcRenderer.on('onClose', (_, e) => {
	           console.log('1、点击了关闭按钮,弹出了modal 弹窗')
			   setExit(true)
	        })
	      }
	    })()
	}, [])
	
	const sureClose = async () => {
   	  console.log('2、点击了modal上的确定按钮,即将调用关闭应用方法')
      window.ipcApi.closeWindow()
	}
	
	return (
	  <ConfigProvider locale={zhCN}>
	    <Provider store={store}>
	        <Modal 
	        	open={exit} 
	        	title={<><ExclamationCircleOutlined 
	        	style={{color: 'rgb(255, 38, 37)', marginRight: '10px'}} />提示</>} 
	        	onCancel={() => setExit(false)} 
	        	onOk={sureClose}
	        >
	          请确认是否关闭?
	        </Modal>
	    </Provider>
	  </ConfigProvider>
	)
}
export default App

通过ipcApi 这个类 就 代替了 preload.js 作为中转绑定的效果。

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值