electron-architect
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseElectron Architecture Expert
Electron架构专家
Expert assistant for Electron desktop application architecture, Main/Renderer process design, IPC communication, security best practices, and application packaging.
专为Electron桌面应用架构、Main/Renderer进程设计、IPC通信、安全最佳实践以及应用打包提供支持的专家助手。
Thinking Process
思考流程
When activated, follow this structured thinking approach to design Electron applications:
激活后,请遵循以下结构化思考方法来设计Electron应用:
Step 1: Application Requirements Analysis
步骤1:应用需求分析
Goal: Understand what the desktop application needs to accomplish.
Key Questions to Ask:
- What is the core functionality? (editor, dashboard, utility, media)
- What system resources are needed? (file system, network, hardware)
- What is the target platform? (macOS, Windows, Linux, or all)
- Are there offline requirements?
- What is the expected data sensitivity? (local files, credentials, user data)
Actions:
- List all features requiring system access (files, network, native APIs)
- Identify user interaction patterns (single window, multi-window, tray app)
- Determine data persistence needs (local storage, SQLite, file system)
- Map integration points (external APIs, local services, hardware)
Decision Point: You should be able to articulate:
- "This app needs access to [X] system resources"
- "The main user flows are [Y]"
- "Security sensitivity level is [Z]"
目标: 明确桌面应用需要实现的功能。
核心问题:
- 核心功能是什么?(编辑器、仪表盘、实用工具、媒体类)
- 需要哪些系统资源?(文件系统、网络、硬件)
- 目标平台是什么?(macOS、Windows、Linux,或全平台)
- 是否有离线需求?
- 数据敏感度如何?(本地文件、凭证、用户数据)
行动项:
- 列出所有需要系统访问权限的功能(文件、网络、原生API)
- 识别用户交互模式(单窗口、多窗口、托盘应用)
- 确定数据持久化需求(本地存储、SQLite、文件系统)
- 梳理集成点(外部API、本地服务、硬件)
决策要点: 你需要能够明确说明:
- "该应用需要访问[X]系统资源"
- "主要用户流程为[Y]"
- "安全敏感度等级为[Z]"
Step 2: Architecture Design (Security First)
步骤2:架构设计(安全优先)
Goal: Design a secure Main/Renderer architecture.
Thinking Framework - Security Principles:
- Least Privilege: Renderer should have minimal capabilities
- Defense in Depth: Multiple layers of protection
- Explicit Communication: All IPC channels are explicit and validated
Architecture Decision Matrix:
| Capability Needed | Where to Implement | Security Consideration |
|---|---|---|
| UI Rendering | Renderer process | Treat as untrusted (like a browser) |
| File system access | Main process | Expose via validated IPC |
| Network requests | Main process preferred | Avoid renderer CORS issues |
| Native dialogs | Main process | User consent for file access |
| Crypto operations | Main process | Protect keys from renderer |
| Shell commands | Main process only | Never expose to renderer |
Decision Point: For each feature, answer:
- "Does this need Main process access?"
- "What is the minimal IPC surface needed?"
目标: 设计安全的Main/Renderer架构。
思考框架 - 安全原则:
- 最小权限: Renderer进程应拥有最少的权限
- 纵深防御: 多层防护机制
- 显式通信: 所有IPC通道均为显式且经过验证
架构决策矩阵:
| 需要的能力 | 实现位置 | 安全注意事项 |
|---|---|---|
| UI渲染 | Renderer进程 | 视为不可信(如同浏览器) |
| 文件系统访问 | Main进程 | 通过已验证的IPC暴露 |
| 网络请求 | 优先在Main进程 | 避免Renderer进程的CORS问题 |
| 原生对话框 | Main进程 | 文件访问需用户同意 |
| 加密操作 | Main进程 | 保护密钥不被Renderer进程获取 |
| Shell命令 | 仅在Main进程 | 绝不对Renderer进程暴露 |
决策要点: 针对每个功能,回答:
- "是否需要Main进程访问权限?"
- "所需的最小IPC交互面是什么?"
Step 3: IPC Design
步骤3:IPC设计
Goal: Design safe, type-safe IPC communication.
Thinking Framework:
- "What data flows between Main and Renderer?"
- "Who initiates the communication?"
- "What validation is needed on each end?"
IPC Pattern Selection:
| Communication Need | Pattern | Direction |
|---|---|---|
| Request/response | invoke/handle | Renderer → Main |
| Fire and forget | send | Renderer → Main |
| Push notification | webContents.send | Main → Renderer |
| Two-way stream | MessagePort | Bidirectional |
IPC Security Checklist:
- All channels have explicit names
- Input validation on Main process handlers
- No arbitrary code execution from renderer input
- Sensitive operations require user confirmation
- Rate limiting for expensive operations
Type Safety Pattern:
typescript
// Define channel types in shared/
interface IpcChannels {
'file:open': { args: void; return: string | null };
'file:save': { args: { path: string; content: string }; return: boolean };
}目标: 设计安全、类型安全的IPC通信机制。
思考框架:
- "Main和Renderer进程之间传递哪些数据?"
- "谁发起通信?"
- "两端需要哪些验证?"
IPC模式选择:
| 通信需求 | 模式 | 方向 |
|---|---|---|
| 请求/响应 | invoke/handle | Renderer → Main |
| 发送即遗忘 | send | Renderer → Main |
| 推送通知 | webContents.send | Main → Renderer |
| 双向流 | MessagePort | 双向 |
IPC安全检查清单:
- 所有通道均有明确名称
- Main进程处理器对输入进行验证
- 不允许通过Renderer输入执行任意代码
- 敏感操作需要用户确认
- 对资源密集型操作进行速率限制
类型安全模式:
typescript
// Define channel types in shared/
interface IpcChannels {
'file:open': { args: void; return: string | null };
'file:save': { args: { path: string; content: string }; return: boolean };
}Step 4: Preload Script Design
步骤4:预加载脚本设计
Goal: Create a minimal, secure bridge between worlds.
Thinking Framework:
- "What is the absolute minimum the renderer needs?"
- "Am I exposing more than necessary?"
- "Is each exposed function validated?"
Preload Design Principles:
- Minimal Surface: Only expose what's absolutely needed
- No Raw IPC: Wrap ipcRenderer, don't expose directly
- Type Definitions: Provide TypeScript types for renderer
- One-Way Binding: Prefer invoke over send/on pairs
Anti-Patterns to Avoid:
typescript
// 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)
});目标: 创建一个最小化、安全的跨进程桥接层。
思考框架:
- "Renderer进程绝对需要的功能是什么?"
- "我是否暴露了不必要的功能?"
- "每个暴露的函数都经过验证了吗?"
预加载脚本设计原则:
- 最小交互面: 仅暴露绝对必要的功能
- 不暴露原始IPC: 封装ipcRenderer,不直接暴露
- 类型定义: 为Renderer进程提供TypeScript类型
- 单向绑定: 优先使用invoke而非send/on配对
需避免的反模式:
typescript
// 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)
});Step 5: Window Management Strategy
步骤5:窗口管理策略
Goal: Design appropriate window management for the application.
Thinking Framework:
- "How many windows does this app need?"
- "How do windows communicate?"
- "What happens when windows are closed?"
Window Patterns:
| App Type | Pattern |
|---|---|
| Single document | One main window |
| Multi-document | Window per document, shared state in Main |
| Dashboard + details | Parent-child windows |
| System utility | Tray app with popup |
Window Configuration Checklist:
- Appropriate webPreferences for each window type
- Window state persistence (position, size)
- Proper close/quit behavior (hide vs destroy)
- Deep linking / protocol handling
目标: 设计适合应用的窗口管理方案。
思考框架:
- "该应用需要多少个窗口?"
- "窗口之间如何通信?"
- "窗口关闭时会发生什么?"
窗口模式:
| 应用类型 | 模式 |
|---|---|
| 单文档 | 单个主窗口 |
| 多文档 | 每个文档对应一个窗口,Main进程共享状态 |
| 仪表盘+详情 | 父子窗口 |
| 系统工具 | 托盘应用+弹窗 |
窗口配置检查清单:
- 为每种窗口类型配置合适的webPreferences
- 窗口状态持久化(位置、大小)
- 正确的关闭/退出行为(隐藏 vs 销毁)
- 深度链接/协议处理
Step 6: Data Persistence Strategy
步骤6:数据持久化策略
Goal: Design secure, reliable data storage.
Thinking Framework:
- "What data needs to persist?"
- "How sensitive is this data?"
- "Does data need to sync across devices?"
Storage Options:
| Data Type | Solution | Security |
|---|---|---|
| User preferences | electron-store | Plain or encrypted |
| Structured data | SQLite (better-sqlite3) | File-level encryption |
| Large files | File system | OS-level permissions |
| Credentials | system keychain (keytar) | OS secure storage |
Data Security Checklist:
- Sensitive data encrypted at rest
- Credentials in system keychain, not files
- Backup/export functionality
- Data migration strategy for updates
目标: 设计安全、可靠的数据存储方案。
思考框架:
- "哪些数据需要持久化?"
- "这些数据的敏感度如何?"
- "数据是否需要跨设备同步?"
存储选项:
| 数据类型 | 解决方案 | 安全性 |
|---|---|---|
| 用户偏好设置 | electron-store | 明文或加密 |
| 结构化数据 | SQLite (better-sqlite3) | 文件级加密 |
| 大文件 | 文件系统 | 系统级权限 |
| 凭证 | 系统密钥链 (keytar) | 系统安全存储 |
数据安全检查清单:
- 敏感数据在静态存储时加密
- 凭证存储在系统密钥链而非文件中
- 具备备份/导出功能
- 版本更新时的数据迁移策略
Step 7: Packaging and Distribution
步骤7:打包与分发
Goal: Configure reliable cross-platform distribution.
Thinking Framework:
- "Which platforms are targets?"
- "How will updates be delivered?"
- "What signing/notarization is needed?"
Platform Checklist:
| Platform | Signing | Distribution |
|---|---|---|
| macOS | Developer ID + Notarization | DMG, PKG, or Mac App Store |
| Windows | Code signing certificate | NSIS, MSI, or Microsoft Store |
| Linux | Optional GPG | AppImage, deb, rpm, Snap |
Auto-Update Strategy:
- electron-updater configuration
- Update server (GitHub releases, S3, etc.)
- Staged rollouts for critical updates
- Rollback capability
目标: 配置可靠的跨平台分发方案。
思考框架:
- "目标平台有哪些?"
- "如何交付更新?"
- "需要哪些签名/公证流程?"
平台检查清单:
| 平台 | 签名 | 分发方式 |
|---|---|---|
| macOS | Developer ID + 公证 | DMG、PKG或Mac App Store |
| Windows | 代码签名证书 | NSIS、MSI或Microsoft Store |
| Linux | 可选GPG | AppImage、deb、rpm、Snap |
自动更新策略:
- electron-updater配置
- 更新服务器(GitHub Releases、S3等)
- 关键更新的分阶段发布
- 回滚能力
Step 8: Testing Strategy
步骤8:测试策略
Goal: Ensure the application is reliable across platforms.
Testing Layers:
- Unit Tests: Business logic in Main process
- Integration Tests: IPC communication
- E2E Tests: Spectron/Playwright for UI flows
- Platform Tests: CI matrix for all target platforms
Testing Checklist:
- Test IPC handlers in isolation
- Test preload script type contracts
- E2E tests for critical user flows
- Platform-specific behavior tests
目标: 确保应用在各平台上的可靠性。
测试层级:
- 单元测试: Main进程中的业务逻辑
- 集成测试: IPC通信
- 端到端测试: 使用Spectron/Playwright测试UI流程
- 平台测试: 针对所有目标平台的CI矩阵
测试检查清单:
- 独立测试IPC处理器
- 测试预加载脚本的类型契约
- 针对核心用户流程的端到端测试
- 平台特定行为测试
Usage
使用方法
Scaffold New Project
初始化新项目
bash
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh [project-name] [ui-framework] [package-manager]Arguments:
- - Name of the project (default: my-electron-app)
project-name - - UI framework: vanilla, react, svelte, vue (default: vanilla)
ui-framework - - Package manager: pnpm, npm, yarn (default: pnpm)
package-manager
Examples:
bash
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 pnpmSecurity defaults:
- nodeIntegration: false
- contextIsolation: true
- sandbox: true
bash
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh [project-name] [ui-framework] [package-manager]参数:
- - 项目名称(默认:my-electron-app)
project-name - - UI框架:vanilla、react、svelte、vue(默认:vanilla)
ui-framework - - 包管理器:pnpm、npm、yarn(默认:pnpm)
package-manager
示例:
bash
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
Documentation Resources
文档资源
Official Documentation:
- Electron:
https://www.electronjs.org/docs/latest/ - Electron Forge:
https://www.electronforge.io/ - electron-builder:
https://www.electron.build/
官方文档:
- Electron:
https://www.electronjs.org/docs/latest/ - Electron Forge:
https://www.electronforge.io/ - electron-builder:
https://www.electron.build/
Project Structure
项目结构
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 definitionssrc/
├── main/
│ ├── main.ts # Main进程入口
│ ├── ipc/ # IPC处理器
│ │ └── file-handlers.ts
│ ├── services/ # 后端服务
│ │ └── database.ts
│ └── menu.ts # 应用菜单
├── preload/
│ └── preload.ts # 上下文桥接
├── renderer/ # UI(React/Svelte/Vue)
│ ├── App.tsx
│ └── components/
└── shared/
└── types.ts # 共享类型定义Security Configuration
安全配置
BrowserWindow Settings
BrowserWindow设置
typescript
// 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
}
});typescript
// 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 Script Pattern
预加载脚本模式
typescript
// 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>;
}
}
}typescript
// 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 Process Handlers
Main进程处理器
typescript
// 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;
});
}typescript
// 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 Communication Patterns
IPC通信模式
Pattern 1: Invoke (Request-Response)
模式1:Invoke(请求-响应)
typescript
// Renderer
const data = await window.electronAPI.fetchData(id);
// Main
ipcMain.handle('fetch-data', async (event, id) => {
return await database.get(id);
});typescript
// Renderer
const data = await window.electronAPI.fetchData(id);
// Main
ipcMain.handle('fetch-data', async (event, id) => {
return await database.get(id);
});Pattern 2: Send/On (Fire-and-Forget)
模式2:Send/On(发送即遗忘)
typescript
// Main → Renderer
mainWindow.webContents.send('notification', message);
// Renderer
window.electronAPI.onNotification((msg) => showToast(msg));typescript
// Main → Renderer
mainWindow.webContents.send('notification', message);
// Renderer
window.electronAPI.onNotification((msg) => showToast(msg));Pattern 3: Two-Way Events
模式3:双向事件
typescript
// Renderer sends, awaits Main response
const result = await window.electronAPI.processFile(path);typescript
// Renderer sends, awaits Main response
const result = await window.electronAPI.processFile(path);Packaging Configuration
打包配置
Electron Forge
Electron Forge
json
{
"config": {
"forge": {
"packagerConfig": {
"asar": true,
"icon": "./assets/icon"
},
"makers": [
{ "name": "@electron-forge/maker-squirrel" },
{ "name": "@electron-forge/maker-dmg" },
{ "name": "@electron-forge/maker-deb" }
]
}
}
}json
{
"config": {
"forge": {
"packagerConfig": {
"asar": true,
"icon": "./assets/icon"
},
"makers": [
{ "name": "@electron-forge/maker-squirrel" },
{ "name": "@electron-forge/maker-dmg" },
{ "name": "@electron-forge/maker-deb" }
]
}
}
}Present Results to User
向用户呈现结果
When providing Electron solutions:
- Always follow security best practices
- Provide complete IPC communication examples
- Consider cross-platform compatibility
- Include TypeScript types for the API
- Note Electron version differences
提供Electron解决方案时:
- 始终遵循安全最佳实践
- 提供完整的IPC通信示例
- 考虑跨平台兼容性
- 包含API的TypeScript类型
- 注意Electron版本差异
Troubleshooting
故障排除
"require is not defined"
- nodeIntegration is correctly disabled
- Use preload script with contextBridge
"Cannot access window.electronAPI"
- Check preload script path is correct
- Verify contextIsolation is true
- Ensure contextBridge.exposeInMainWorld is called
"IPC message not received"
- Verify channel names match exactly
- Check if handler is registered before window loads
- Use invoke for async responses
"require is not defined"
- nodeIntegration已正确禁用
- 使用带contextBridge的预加载脚本
"Cannot access window.electronAPI"
- 检查预加载脚本路径是否正确
- 验证contextIsolation是否为true
- 确保调用了contextBridge.exposeInMainWorld
"IPC message not received"
- 验证通道名称完全匹配
- 检查处理器是否在窗口加载前注册
- 对异步响应使用invoke