electron-architect

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Electron 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:
  1. List all features requiring system access (files, network, native APIs)
  2. Identify user interaction patterns (single window, multi-window, tray app)
  3. Determine data persistence needs (local storage, SQLite, file system)
  4. 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,或全平台)
  • 是否有离线需求?
  • 数据敏感度如何?(本地文件、凭证、用户数据)
行动项:
  1. 列出所有需要系统访问权限的功能(文件、网络、原生API)
  2. 识别用户交互模式(单窗口、多窗口、托盘应用)
  3. 确定数据持久化需求(本地存储、SQLite、文件系统)
  4. 梳理集成点(外部API、本地服务、硬件)
决策要点: 你需要能够明确说明:
  • "该应用需要访问[X]系统资源"
  • "主要用户流程为[Y]"
  • "安全敏感度等级为[Z]"

Step 2: Architecture Design (Security First)

步骤2:架构设计(安全优先)

Goal: Design a secure Main/Renderer architecture.
Thinking Framework - Security Principles:
  1. Least Privilege: Renderer should have minimal capabilities
  2. Defense in Depth: Multiple layers of protection
  3. Explicit Communication: All IPC channels are explicit and validated
Architecture Decision Matrix:
Capability NeededWhere to ImplementSecurity Consideration
UI RenderingRenderer processTreat as untrusted (like a browser)
File system accessMain processExpose via validated IPC
Network requestsMain process preferredAvoid renderer CORS issues
Native dialogsMain processUser consent for file access
Crypto operationsMain processProtect keys from renderer
Shell commandsMain process onlyNever expose to renderer
Decision Point: For each feature, answer:
  • "Does this need Main process access?"
  • "What is the minimal IPC surface needed?"
目标: 设计安全的Main/Renderer架构。
思考框架 - 安全原则:
  1. 最小权限: Renderer进程应拥有最少的权限
  2. 纵深防御: 多层防护机制
  3. 显式通信: 所有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 NeedPatternDirection
Request/responseinvoke/handleRenderer → Main
Fire and forgetsendRenderer → Main
Push notificationwebContents.sendMain → Renderer
Two-way streamMessagePortBidirectional
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/handleRenderer → Main
发送即遗忘sendRenderer → Main
推送通知webContents.sendMain → 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:
  1. Minimal Surface: Only expose what's absolutely needed
  2. No Raw IPC: Wrap ipcRenderer, don't expose directly
  3. Type Definitions: Provide TypeScript types for renderer
  4. 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进程绝对需要的功能是什么?"
  • "我是否暴露了不必要的功能?"
  • "每个暴露的函数都经过验证了吗?"
预加载脚本设计原则:
  1. 最小交互面: 仅暴露绝对必要的功能
  2. 不暴露原始IPC: 封装ipcRenderer,不直接暴露
  3. 类型定义: 为Renderer进程提供TypeScript类型
  4. 单向绑定: 优先使用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 TypePattern
Single documentOne main window
Multi-documentWindow per document, shared state in Main
Dashboard + detailsParent-child windows
System utilityTray 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 TypeSolutionSecurity
User preferenceselectron-storePlain or encrypted
Structured dataSQLite (better-sqlite3)File-level encryption
Large filesFile systemOS-level permissions
Credentialssystem 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:
PlatformSigningDistribution
macOSDeveloper ID + NotarizationDMG, PKG, or Mac App Store
WindowsCode signing certificateNSIS, MSI, or Microsoft Store
LinuxOptional GPGAppImage, deb, rpm, Snap
Auto-Update Strategy:
  • electron-updater configuration
  • Update server (GitHub releases, S3, etc.)
  • Staged rollouts for critical updates
  • Rollback capability
目标: 配置可靠的跨平台分发方案。
思考框架:
  • "目标平台有哪些?"
  • "如何交付更新?"
  • "需要哪些签名/公证流程?"
平台检查清单:
平台签名分发方式
macOSDeveloper ID + 公证DMG、PKG或Mac App Store
Windows代码签名证书NSIS、MSI或Microsoft Store
Linux可选GPGAppImage、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:
  • project-name
    - Name of the project (default: my-electron-app)
  • ui-framework
    - UI framework: vanilla, react, svelte, vue (default: vanilla)
  • package-manager
    - Package manager: pnpm, npm, yarn (default: pnpm)
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 pnpm
Security defaults:
  • nodeIntegration: false
  • contextIsolation: true
  • sandbox: true
bash
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
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 definitions
src/
├── 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