前言

实际上electron是基于chromium和nodejs的一个框架,所以我们在开发electron应用的时候,可以同时使用浏览器的api和nodejs的api。因为最近组内安排了上位机开发的活,又不会C#,所以就想到了electron,毕竟我对前端的东西还算比较熟悉一些,写js的总想用js做所有事情。也就应了那句话

Atwood’s Law: “Any application that can be written in JavaScript, will eventually be written in JavaScript.”

两个核心进程

electron应用主要有两个核心进程:主进程(main process)和渲染进程(renderer process)。

  • 主进程:负责管理应用的生命周期、创建和管理浏览器窗口、处理系统级事件等。主进程运行在Node.js环境中,可以访问操作系统的底层API。
  • 渲染进程:负责渲染用户界面,处理用户交互等。每个浏览器窗口都有一个独立的渲染进程,运行在Chromium的渲染引擎中,主要使用HTML、CSS和JavaScript来构建用户界面。

两个进程就意味着存在进程通信,electron提供了ipcMainipcRenderer模块来实现主进程和渲染进程之间的通信。封装起来了,也是很简单的事件机制。
主进程

1
2
3
4
5
6
// 主进程
const { ipcMain } = require('electron');
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg); // 打印来自渲染进程的消息
event.reply('asynchronous-reply', 'pong'); // 回复渲染进程
});

渲染进程

1
2
3
4
5
6
// 渲染进程
const { ipcRenderer } = require('electron');
ipcRenderer.send('asynchronous-message', 'ping'); // 发送消息到主进程
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg); // 打印来自主进程的回复
});

按道理是这样的,但是,直接这样把主进程里的东西暴露给渲染进程是不安全的,毕竟渲染进程是可以加载远程内容的,万一加载了恶意代码,那就完蛋了。所以electron推荐使用contextBridge来暴露安全的API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 主进程
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => {
// 只允许特定的频道
let validChannels = ['toMain'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ['fromMain'];
if (validChannels.includes(channel)) {
// 监听来自主进程的消息
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
});

然后在渲染进程中就可以通过window.api来访问这些安全的API了。

1
2
3
4
5
// 渲染进程
window.api.send('toMain', 'Hello from renderer');
window.api.receive('fromMain', (data) => {
console.log(`Received ${data} from main process`);
});

工程化

工程脚手架这里使用的是electron-vite,这个脚手架是基于vite的,vite是一个非常快的前端构建工具,使用了原生的ES模块,热更新非常快。electron-vite就是把viteelectron结合起来了,提供了一套完整的开发和构建流程。
electron-vite文档