前言
electron分了主进程和渲染进程, 如果在主进程中执行的时间过长会导致页面卡顿,因为主进程管理者渲染器进程,为了优化,做了很多的一个方案:
1. 再开一个BrowserWindow 窗口来处理这些逻辑,设置这个窗口隐藏,然后通过electron内置的ipc进行消息的通讯
2. 利用webWorker来处理这些复杂的逻辑
3. 使用golang等处理业务,打包成exe,使用node的child_process启动exe,通过socket处理
4. 通过的node的fork启动一个node服务来处理逻辑
...
像1、3、4这种本质上是要开多一个进程去处理,那么进程之间的通信就很重要了,比如socket,ipcRender,ipcMain。
我有个项目就用了socket做了通信,不过用socket做通信可能会遇到端口占用的情况,就是你先跑了socket,然后端口被其他应用占掉,这时注意重新拉起。
命名管道通信
回归正题,现在做一个使用net模块实现electron主进程和node进程的命名管道通信,功能比较简单,就是获取文件夹下所有的文件和文件夹。
这边读取文件夹下所有文件和文件夹作为Server,electron主进程是client
fileDirRead.js:
const net = require("net");
const pipeFile =
process.platform === "win32" ? "\\\\.\\pipe\\rfpip" : "/tmp/rfpip.sock";
const fs = require("fs");
const path = require("path");
const readDir = (dirPath, arr) => {
const dirs = [];
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const localPath = path.join(dirPath, file);
if (fs.statSync(localPath).isDirectory()) {
dirs.push(localPath);
}
arr.push(localPath);
});
while (dirs.length > 0) {
const dir = dirs.shift();
const parent = dir;
const files = fs.readdirSync(dir);
files.forEach((file) => {
const localPath = path.join(parent, file);
if (fs.statSync(localPath).isDirectory()) {
dirs.push(localPath);
}
arr.push(localPath);
});
}
};
const server = net.createServer((connection) => {
connection.on("close", () => console.log("stop"));
connection.on("data", (data) => {
const arr =[];
readDir(data.toString(),arr);
connection.write(JSON.stringify(arr));
});
connection.on("error", (err) => console.error(err.message));
});
try {
fs.unlinkSync(pipeFile);
} catch (error) {
console.log("error:", error.message);
}
server.listen(pipeFile);
main.js:
const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const net = require("net");
let pipClient = null;
let client = null;
let mainWindow = null;
const { fork } = require("child_process");
...
const startNodeProcess = () => {
client = fork(path.resolve("./fileDirRead.js"));
client.on("close", () => {
console.log("客户端关闭")
client = null;
});
};
const pipConnect = () => {
if (client) {
const pipeFile =
process.platform === "win32" ? "\\\\.\\pipe\\rfpip" : "/tmp/rfpip.sock";
setTimeout(() =>{
pipClient = net.connect(pipeFile);
pipClient.on("connect", () => console.log("连接"));
pipClient.on("data", (data) => {
if (mainWindow) {
mainWindow.webContents.send("getDir",data.toString())
}
});
pipClient.on("close", () => console.log("关闭"));
},400)
}
};
app.whenReady().then(() => {
createWindow();
// 启动
startNodeProcess();
pipConnect();
ipcMain.on("readdir", (event, dirPath) => {
if (pipClient) {
pipClient.write(dirPath)
}
});
});
renderer.js:
const ipcRenderer = window.require("electron").ipcRenderer
const button = document.getElementById("button");
button.addEventListener('click',() => {
input = document.getElementById("text");
const data=input.value;
console.log("发送",data)
ipcRenderer.send("readdir",data);
})
ipcRenderer.on("getDir",(event,data)=>{
const frame = document.createDocumentFragment();
const lis = JSON.parse(data);
lis.forEach(item=>{
const li =document.createElement("li");
li.innerText = item;
frame.appendChild(li);
})
const ul = document.getElementById("ul");
ul.innerHTML = "";
ul.appendChild(frame);
})
index.html:
<!DOCTYPE html>
<html>
<head>
....
</head>
<body>
<h1>读取文件</h1>
<input type="text" id="text" />
<button id="button">读取</button>
<ul id="ul">
</ul>
<!-- You can also require other files to run in this process -->
<script src="./renderer.js"></script>
</body>
</html>
效果:
总结
大概就是这样子了,当然就有点坑,比如乱码什么的,