Electorn由一个主线程(main), 调动多个渲染进程(renderer), 其中涉及到进程间通信ipc,我个人理解很像顶层window与多个iframe之间的通讯。
推荐使用electron-better-ipc,提供更好的封装和语义化的api。
有一些公共的事件,或者需要统一处理的业务,虽然是在渲染页面触发的,但是可以选择通过ipc传递给主线程统一处理,避免这种比较底层的代码散落在各个渲染进程。ipc提供了一个新的思路! 可以多尝试(虽然有些大量数据的不适用)。
记录部分作为运行日志的代码
import { ipcMain, ipcRenderer } from 'electron-better-ipc';
import fsPromises from 'node:fs/promises';
import path from 'path';
import dayjs from 'dayjs';
// 声明数据结构
type ILogData = {
level: 'ERROR' | 'DEBUG', // 这里的类型借鉴了log4js
name: string; // 主要描述错误的模块位置,函数名,请求名
data: any; // 携带的信息,err对象等
};
// 主线程监听事件
ipcMain.on('save-log', async(_event, logData: ILogData) => {
await commitLog(logData);
});
ipcMain.on('save-log', async (_event, logData: ILogData) => {
await saveLog(logData);
});
ipcMain.on('clear-log', async (_event, p?: string) => {
await clearLog(p);
});
以下是具体实现
// 上报日志
const commitLog = async (logData: ILogData) => {
const { level, name, data } = logData;
await saveLog(logData);
/** 这里是后端或者钉钉机器人的接口 */
await comit2Robot(level, name, data);
};
// 保存日志文件
const saveLog = async (logData: ILogData) => {
try {
const { level, name, overload } = logData;
if (!level || !name) return;
const logPath = path.join(dataFolderPath, 'logger', dayjs().format(YMD));
const filePath = path.join(
dataFolderPath,
'logger',
dayjs().format(YMD),
`[${level}] ${name}.txt`,
); // 以事件名+类型作为文件名
(logData as any).time = dayjs().format('HH-mm-ss'); // 额外添加时间字段
const txt = `${JSON.stringify(logData)}\n\n`;
fsPromises
.access(filePath)
.then(async () => {
// 如果文件存在: 覆盖 或 追加
if (overload) {
await fsPromises.writeFile(filePath, txt, {
encoding: 'utf-8',
flag: 'w',
});
} else {
await fsPromises.appendFile(filePath, txt, {
encoding: 'utf-8',
});
}
})
.catch(async () => {
// 如果文件不存在, 则先创建再写入。
await fsPromises.mkdir(logPath, { recursive: true });
await fsPromises.writeFile(filePath, txt, {
encoding: 'utf-8',
flag: 'a',
});
});
} catch (error) {
console.log('保存日志失败');
console.warn(error);
}
};