我们在做Electron
项目时,会遇到主进程与渲染进程通信的场景,那么具体怎么实现呢,我们今天用一个demo
来表现下通信过程,核心业务为:项目过多,用户添加项目目标文件夹到列表,进而对项目进行统一管理,我们这里提供2种方式
效果图:
main.js
主进程,创建一个顶部菜单,打开一个文件夹,进而通知渲染进程
const { app, BrowserWindow, ipcMain, Menu, dialog, protocol } = require('electron')
let mainWindow // 保存当前的主窗口回调,用于发布消息
// 创建菜单列表,添加自定义菜单,并且快捷键为ctrl + w
let menuTemplate = [
// 一级菜单
{
label: "文件",
role: 'fileMenu',
},
{
label: "视图",
role: 'viewMenu'
},
{
label: '窗口',
role: 'windowMenu'
},
{
label: '添加项目',
click: () => {
handle_click()
},
accelerator: 'ctrl + w'
}
];
// 用于构建MenuItem
let menuBuilder = Menu.buildFromTemplate(menuTemplate);
// setApplicationMenu 在macOS上将 menu设置成应用内菜单 在windows和Linux上,menu 将会被设置成窗口顶部菜单
Menu.setApplicationMenu(menuBuilder);
function createWindow() {
// 创建浏览器窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600,
// fullscreen: true,
webPreferences: {
preload: path.resolve(__dirname, './preload.js'),
devTools: false,
webSecurity: false,
enableRemoteModule: true,
nodeIntegration : true, //允许渲染进程使用Nodejs
contextIsolation : false //允许渲染进程使用Nodejs
// sandbox: false // 禁用沙盒
},
frame: true,
alwaysOnTop: false
})
}
当用户执行 ctrl + w
或者点击添加项目时,触发 handle_click
方法:
// 点击添加项目
function handle_click() {
dialog.showOpenDialog({
title: '添加文件夹',
properties: ['openDirectory'],
message: '添加SVN项目'
})
.then(res => {
// res.canceled为取消,反之未取消,返回filePaths物理路径
if (!res.canceled) {
// 调用webContents发送消息,update_list为标识,类似订阅发布者的指定key
mainWindow.webContents.send('update_list', res.filePaths)
}
})
}
preload.js
注册 ipcRenderer
方法,ipcRenderer
是一个 EventEmitter
的实例。 你可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息
// 进程间通信模块
const { contextBridge, ipcRenderer } = require('electron/renderer')
// 所有Node.js API都可以在预加载过程中使用。
// 它拥有与Chrome扩展一样的沙盒。
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})
// 注册
window.electron = {
ipcRenderer
}
最后在 renderer.js
监听消息
const child_process = require('child_process')
window.electron.ipcRenderer.on('update_list', (_event, value) => {
// 调用node子进程
child_process.exec(`TortoiseProc.exe /command:commit /path ${value}`, (err, stdout, stderr) => {
console.log(stdout)
})
}
index.html
中引用renderer.js
,最终将渲染指向index.html
// 判断当前的环境,如果是开发环境,反之生产环境
if (!app.isPackaged) {
mainWindow.loadURL(path.resolve(__dirname, './dist/index.html'))
} else {
mainWindow.loadURL(path.resolve(__dirname, './dist/index.html'))
}
其次就是利用 http
服务,搭建一个服务器,通过请求的方式通知,这里就不过多说了,直接上代码
let serve = express()
let http = require('http').Server(serve)
//设置CORS
serve.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Credentials','true');
next();
});
serve.get('/', (req, res) => res.send('Hello'))
serve.get('/file', (req, res) => {
// 调用选择文件夹方法,并将结果返回
})
// 这里必须http监听 否则客户端会报404
let server = http.listen(8009, '127.0.0.1', () => {
let host = server.address().address
let port = server.address().port
console.log(`Server running at http://${host}:${port}`)
})