Appearance
进程间通信
进程间通信是通过预加载脚本来联系主进程和渲染进程的
创建预加脚本
这里我所有的通信都通过三个方式进行
js
// mainPreload.mjs
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('mainAPI', {
sendToMain: (params) => ipcRenderer.send('fromRender', params), // 用于渲染进程发送到主进程 params:{event:"xxx",args:XXX}
fromMain: (callback) => ipcRenderer.on('sendToRender', callback), // 用于主进程发送到渲染进程 win.webContents.send('sendToRender', params)
invokeMain: (params) => ipcRenderer.invoke('eventRender', params), // 用于渲染进程调用主进程方法 window.mainAPI.invokeMain({event:"xxx",args:XXX})
})
加载预加载脚本
js
// index.js
webPreferences: {
nodeIntegration: true,
contextIsolation: true, // 需开启
enableRemoteModule: true,
preload: path.join(__dirname, 'mainPreload.mjs')
}
注册接收渲染进程的事件,和发送信息到渲染进程
以下为当前electron/index.js完整代码
js
import { app, BrowserWindow, ipcMain } from 'electron'
import os from 'os'
import path, { dirname } from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
let mainWindow
function createWindow () {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
enableRemoteModule: true,
preload: path.join(__dirname, 'mainPreload.mjs')
}
})
loadPage()
// 监听页面加载完成
mainWindow.webContents.on('did-finish-load', () => {
// 发送数据到渲染
mainWindow.webContents.send('sendToRender', { event: 'init', data: 'main init' })
});
}
function loadPage() {
if (app.isPackaged) {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
// mainWindow.loadURL('http://localhost:8100/')
// mainWindow.webContents.openDevTools()
} else {
mainWindow.webContents.openDevTools()
mainWindow.loadURL('http://localhost:8100/')
}
}
/**
* 当应用准备好时
*/
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
ipcMain.on('fromRender', (_, params) => {
console.log('fromRender', params)
switch (params.event) {
case 'print':
mainWindow.webContents.print()
break
}
})
ipcMain.handle('eventRender', (_, params) => {
switch (params.event) {
case 'hostInfo':
return {
hostname: os.hostname(),
arch: os.arch(),
platform: os.platform(),
release: os.release(),
version: os.version()
}
}
})
在渲染进程中接收或发送
tsx
import React, { useEffect, useState } from 'react';
import { Button } from 'antd';
const isElectron = ()=>{
// 判断是否运行在electron环境中
return window && window.mainAPI && window.mainAPI.invokeMain
}
const Home: React.FC = () => {
const [text, setText] = useState('')
useEffect(() => {
// 组件加载完成,监听主进程发送过来的数据
if (isElectron()){
window.mainAPI.fromMain((_: any, data: any) => {
switch (data.event) {
case 'init':
console.log('收到主进程发送的数据', data.data);
break;
}
})
}
}, [])
const handleSend = () => {
if (!isElectron()) return
// 发送数据到主进程
window.mainAPI.sendToMain({ 'event': 'update', args: { newVersion:' 1.0.2' }})
}
const handleGetHostInfo = () => {
if (!isElectron()) return
window.mainAPI.invokeMain({ 'event': 'hostInfo' }).then((res: any) => {
console.log('收到主进程发送的数据', res);
setText("系统信息"+JSON.stringify(res))
})
}
return(
<div>
<div>
<Button style={{ marginRight: 10 }} type="primary" onClick={handleSend}>发送数据到主进程</Button>
<Button style={{ marginRight: 10 }} onClick={handleGetHostInfo}>获取系统信息</Button>
</div>
<div>{text}</div>
</div>
)
};
export default Home;
注意:我在这里渲染进程是ts环境的,需要给window添加类型定义,
ts// electron.d.ts declare global { interface Window { // eslint-disable-next-line @typescript-eslint/no-explicit-any mainAPI: { sendToMain: (params: any) => void; fromMain: (callback: (event:any, params: any) => void) => void; invokeMain: (params: any) => Promise<any>; }; } } export {};
注意:electron使用ESM规范
- 我这里是基于ESM规范写的,electron官方要electron28才支持
- 预加载脚本需为.mjs文件
- 需解决 __dirname 不存在的问题,解决方法主要为以下几行代码
js
import path, { dirname } from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)