关于 electron-architect
electron-architect 是面向 Electron 开发者的架构规划技能,适用于设计新桌面应用或审视现有方案。它结合安全优先的主进程、渲染进程与 IPC 指引,并提供项目脚手架及持久化、测试和分发建议。
主要功能
- 01需求与架构分析
- 02安全的主进程与渲染进程设计
- 03经过验证的类型化 IPC 模式
- 04预加载桥接设计指引
- 05支持 vanilla、React、Svelte 或 Vue 的脚手架
- 06打包与测试检查清单
适用场景
- 01规划 Electron 应用的进程边界
- 02设计精简且经过验证的 IPC API
- 03搭建新的 Electron 项目
- 04审查安全、存储或发布方案
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tomlord1122/ tomtom-skill --skill electron-architect安装后在 Claude Code 中输入/electron-architect确认已加载。
附带文件scripts/scaffold-project.sh
详细介绍
Electron 架构专家
在 Electron 桌面应用架构、主进程/渲染进程设计、IPC 通信、安全最佳实践和应用打包方面提供专业帮助的助手。
思考流程
激活后,按照以下结构化思考流程设计 Electron 应用:
第 1 步:应用需求分析
目标: 了解桌面应用需要实现什么功能。
需要提出的关键问题:
- 核心功能是什么?(编辑器、仪表板、实用工具、媒体)
- 需要哪些系统资源?(文件系统、网络、硬件)
- 目标平台是什么?(macOS、Windows、Linux 或全部平台)
- 是否有离线需求?
- 预期的数据敏感程度如何?(本地文件、凭据、用户数据)
操作:
- 列出所有需要访问系统的功能(文件、网络、原生 API)
- 识别用户交互模式(单窗口、多窗口、托盘应用)
- 确定数据持久化需求(本地存储、SQLite、文件系统)
- 梳理集成点(外部 API、本地服务、硬件)
决策点: 你应该能够明确说明:
- “此应用需要访问 [X] 系统资源”
- “主要用户流程是 [Y]”
- “安全敏感级别为 [Z]”
第 2 步:架构设计(安全优先)
目标: 设计安全的主进程/渲染进程架构。
思考框架——安全原则:
- 最小权限: 渲染进程应具备尽可能少的能力
- 纵深防御: 设置多层保护
- 明确通信: 所有 IPC 通道都必须明确指定并经过验证
架构决策矩阵:
| 所需能力 | 实现位置 | 安全注意事项 |
|---|---|---|
| UI 渲染 | 渲染进程 | 视为不可信环境(如同浏览器) |
| 文件系统访问 | 主进程 | 通过经过验证的 IPC 暴露 |
| 网络请求 | 优先在主进程中处理 | 避免渲染进程的 CORS 问题 |
| 原生对话框 | 主进程 | 访问文件需获得用户同意 |
| 加密操作 | 主进程 | 防止渲染进程访问密钥 |
| Shell 命令 | 仅限主进程 | 绝不向渲染进程暴露 |
决策点: 针对每项功能,回答:
- “这项功能需要访问主进程吗?”
- “所需的最小 IPC 接口范围是什么?”
第 3 步:IPC 设计
目标: 设计安全且类型安全的 IPC 通信。
思考框架:
- “主进程和渲染进程之间传输哪些数据?”
- “由哪一方发起通信?”
- “两端分别需要进行哪些验证?”
IPC 模式选择:
| 通信需求 | 模式 | 方向 |
|---|---|---|
| 请求/响应 | invoke/handle | 渲染进程 → 主进程 |
| 发送后不等待回复 | send | 渲染进程 → 主进程 |
| 推送通知 | webContents.send | 主进程 → 渲染进程 |
| 双向流 | MessagePort | 双向 |
IPC 安全检查清单:
- 所有通道都具有明确名称
- 在主进程处理程序中验证输入
- 不根据渲染进程的输入执行任意代码
- 敏感操作需获得用户确认
- 对开销较大的操作进行速率限制
类型安全模式:
// Define channel types in shared/
interface IpcChannels {
'file:open': { args: void; return: string | null };
'file:save': { args: { path: string; content: string }; return: boolean };
}
第 4 步:预加载脚本设计
目标: 在两个环境之间建立精简且安全的桥接。
思考框架:
- “渲染进程实际需要的最少能力是什么?”
- “我是否暴露了不必要的内容?”
- “每个暴露的函数是否都经过验证?”
预加载设计原则:
- 精简接口: 只暴露绝对必要的内容
- 不直接暴露原始 IPC: 封装 ipcRenderer,而不是直接暴露
- 类型定义: 为渲染进程提供 TypeScript 类型
- 单向绑定: 优先使用 invoke,而不是 send/on 组合
应避免的反模式:
// BAD: Exposes raw ipcRenderer
contextBridge.exposeInMainWorld('electron', { ipcRenderer });
// BAD: Arbitrary channel execution
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => ipcRenderer.send(channel, data)
});
// GOOD: Explicit, limited API
contextBridge.exposeInMainWorld('api', {
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (content: string) => ipcRenderer.invoke('file:save', content)
});
步骤 5:窗口管理策略
目标: 为应用设计合适的窗口管理方式。
思考框架:
- “此应用需要多少个窗口?”
- “窗口之间如何通信?”
- “窗口关闭时会发生什么?”
窗口模式:
| 应用类型 | 模式 |
|---|---|
| 单文档 | 一个主窗口 |
| 多文档 | 每个文档一个窗口,共享状态存储在 Main 中 |
| 仪表板 + 详情 | 父子窗口 |
| 系统工具 | 带弹出窗口的托盘应用 |
窗口配置检查清单:
- 为每种窗口类型配置合适的 webPreferences
- 持久化窗口状态(位置、大小)
- 正确处理关闭/退出行为(隐藏还是销毁)
- 深层链接 / 协议处理
步骤 6:数据持久化策略
目标: 设计安全、可靠的数据存储方案。
思考框架:
- “哪些数据需要持久化?”
- “这些数据有多敏感?”
- “数据是否需要在设备间同步?”
存储选项:
| 数据类型 | 解决方案 | 安全性 |
|---|---|---|
| 用户偏好设置 | electron-store | 明文或加密 |
| 结构化数据 | SQLite (better-sqlite3) | 文件级加密 |
| 大型文件 | 文件系统 | 操作系统级权限 |
| 凭据 | 系统密钥链 (keytar) | 操作系统安全存储 |
数据安全检查清单:
- 对静态敏感数据进行加密
- 将凭据存储在系统密钥链中,而非文件中
- 提供备份/导出功能
- 制定更新时的数据迁移策略
步骤 7:打包与分发
目标: 配置可靠的跨平台分发方案。
思考框架:
- “目标平台有哪些?”
- “如何发布更新?”
- “需要哪些签名/公证?”
平台检查清单:
| 平台 | 签名 | 分发方式 |
|---|---|---|
| macOS | Developer ID + 公证 | DMG、PKG 或 Mac App Store |
| Windows | 代码签名证书 | NSIS、MSI 或 Microsoft Store |
| Linux | GPG(可选) | AppImage、deb、rpm、Snap |
自动更新策略:
- 配置 electron-updater
- 更新服务器(GitHub releases、S3 等)
- 对关键更新进行分阶段发布
- 支持回滚
步骤 8:测试策略
目标: 确保应用在各平台上都可靠运行。
测试层级:
- 单元测试: Main 进程中的业务逻辑
- 集成测试: IPC 通信
- 端到端测试: 使用 Spectron/Playwright 测试 UI 流程
- 平台测试: 为所有目标平台配置 CI 矩阵
测试检查清单:
- 独立测试 IPC 处理程序
- 测试 preload 脚本的类型契约
- 为关键用户流程编写端到端测试
- 测试特定平台的行为
使用方法
搭建新项目
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh [project-name] [ui-framework] [package-manager]
参数:
project-name- 项目名称(默认:my-electron-app)ui-framework- UI 框架:vanilla、react、svelte、vue(默认:vanilla)package-manager- 包管理器:pnpm、npm、yarn(默认:pnpm)
示例:
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app react
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app svelte pnpm
安全默认设置:
- nodeIntegration: false
- contextIsolation: true
- sandbox: true
文档资源
官方文档:
- Electron:
https://www.electronjs.org/docs/latest/ - Electron Forge:
https://www.electronforge.io/ - electron-builder:
https://www.electron.build/
项目结构
src/
├── main/
│ ├── main.ts # Main process entry
│ ├── ipc/ # IPC handlers
│ │ └── file-handlers.ts
│ ├── services/ # Backend services
│ │ └── database.ts
│ └── menu.ts # Application menu
├── preload/
│ └── preload.ts # Context bridge
├── renderer/ # UI (React/Svelte/Vue)
│ ├── App.tsx
│ └── components/
└── shared/
└── types.ts # Shared type definitions
安全配置
BrowserWindow 设置
// main.ts - Secure configuration
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false, // Disable Node.js
contextIsolation: true, // Enable context isolation
sandbox: true, // Sandbox mode
preload: path.join(__dirname, 'preload.js'),
webSecurity: true, // Enforce same-origin
}
});
Preload 脚本模式
// preload.ts - Safe API exposure
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
// One-way: Renderer → Main
saveFile: (content: string) =>
ipcRenderer.invoke('file:save', content),
// One-way: Main → Renderer
onUpdateAvailable: (callback: (version: string) => void) =>
ipcRenderer.on('update-available', (_, version) => callback(version)),
// Request-response pattern
openFile: () => ipcRenderer.invoke('dialog:openFile'),
});
// Type declaration for renderer
declare global {
interface Window {
electronAPI: {
saveFile: (content: string) => Promise<boolean>;
onUpdateAvailable: (callback: (version: string) => void) => void;
openFile: () => Promise<string | null>;
}
}
}
Main 进程处理程序
// main/ipc/file-handlers.ts
import { ipcMain, dialog } from 'electron';
import { readFile, writeFile } from 'fs/promises';
export function registerFileHandlers() {
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
});
if (canceled) return null;
return readFile(filePaths[0], 'utf-8');
});
ipcMain.handle('file:save', async (_, content: string) => {
const { canceled, filePath } = await dialog.showSaveDialog({});
if (canceled || !filePath) return false;
await writeFile(filePath, content);
return true;
});
}
IPC 通信模式
模式 1:Invoke(请求-响应)
// Renderer
const data = await window.electronAPI.fetchData(id);
// Main
ipcMain.handle('fetch-data', async (event, id) => {
return await database.get(id);
});
模式 2:Send/On(即发即弃)
// Main → Renderer
mainWindow.webContents.send('notification', message);
// Renderer
window.electronAPI.onNotification((msg) => showToast(msg));
模式 3:双向事件
// Renderer sends, awaits Main response
const result = await window.electronAPI.processFile(path);
打包配置
Electron Forge
{
"config": {
"forge": {
"packagerConfig": {
"asar": true,
"icon": "./assets/icon"
},
"makers": [
{ "name": "@electron-forge/maker-squirrel" },
{ "name": "@electron-forge/maker-dmg" },
{ "name": "@electron-forge/maker-deb" }
]
}
}
}
向用户呈现结果
提供 Electron 解决方案时:
- 始终遵循安全最佳实践
- 提供完整的 IPC 通信示例
- 考虑跨平台兼容性
- 为 API 添加 TypeScript 类型
- 说明 Electron 版本差异
故障排除
“require 未定义”
- 确认已正确禁用 nodeIntegration
- 使用带有 contextBridge 的预加载脚本
“无法访问 window.electronAPI”
- 检查预加载脚本路径是否正确
- 确认 contextIsolation 为 true
- 确保已调用 contextBridge.exposeInMainWorld
“未收到 IPC 消息”
- 确认通道名称完全匹配
- 检查窗口加载前是否已注册处理程序
- 异步响应请使用 invoke
评论
支持 Markdown