进程模型
Electron 基于 Chromium 构建,也采用了类似的多进程架构。
单进程优缺点:
- 尽可能资源共享,额外开销小
- 隔离性差,一个页面崩溃会影响整个浏览器
Electron 进程分为 main 和 renderer 两类。
主进程 main
每个 Electron 应用只有 1 个主进程,是应用的入口,运行在 Node 环境。
负责:
- 管理窗口
BrowserWindow
- 管理渲染进程和页面
webContents
- 管理生命周期
app
- 提供原生 API
- 菜单/对话框
- 授权操作
不能:
- 访问和操作 DOM
渲染进程 renderer
每个打开的窗口对应一个渲染进程,运行在 Chromium 环境。
负责:
- 管理前端页面
不能:
- 访问 Node 环境和原生 API
preload 脚本
在渲染进程中、页面加载之前执行,允许访问部分 Node API。
在主脚本中附加到窗口上:
js
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js',
},
});
contextIsolation
出于安全原因,preload 脚本与页面是隔离的,二者访问的 window
不是同一个。
需要使用 contextBridge
进行变量的传递,相比直接设置 window
有更多限制、更加安全。
尽管 contextBridge
提供了安全方面的限制,但开发者还需要尽可能收缩权限,避免直接传递功能强大的 IPC 函数。
contextBridge
设置页面的全局变量:
js
// preload.js
contextBridge.exposeInMainWorld('myAPI', {
desktop: true,
});
// renderer.js
console.log(window.myAPI);
// => { desktop: true }
TS 支持
通过 contextBridge
传递的属性需要手动添加类型定义。
ts
// preload.ts
contextBridge.exposeInMainWorld('electronAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs'),
});
// renderer.d.ts
export interface IElectronAPI {
loadPreferences: () => Promise<void>;
}
declare global {
interface Window {
electronAPI: IElectronAPI;
}
}
创建子进程
- Electron 提供了
utilityProcess
用于创建子进程 - 相比 Node 原生的
child_process.fork
,能够更方便地与渲染进程通信 (通过MessagePort
) - 因此在 Electron 应用中创建子进程时优先使用
utilityProcess
类型优化
Electron 包的子路径提供了更完善的类型定义。
js
const { app } = require('electron/main');
const { shell } = require('electron/common');