mcpField
E
electron-architect

electron-architect

github.com/tomlord1122开发0150 次安装6 周前更新
代码仓库

关于 electron-architect

electron-architect 是面向 Electron 开发者的架构规划技能,适用于设计新桌面应用或审视现有方案。它结合安全优先的主进程、渲染进程与 IPC 指引,并提供项目脚手架及持久化、测试和分发建议。

主要功能

  1. 01需求与架构分析
  2. 02安全的主进程与渲染进程设计
  3. 03经过验证的类型化 IPC 模式
  4. 04预加载桥接设计指引
  5. 05支持 vanilla、React、Svelte 或 Vue 的脚手架
  6. 06打包与测试检查清单

适用场景

  1. 01规划 Electron 应用的进程边界
  2. 02设计精简且经过验证的 IPC API
  3. 03搭建新的 Electron 项目
  4. 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 或全部平台)
  • 是否有离线需求?
  • 预期的数据敏感程度如何?(本地文件、凭据、用户数据)

操作:

  1. 列出所有需要访问系统的功能(文件、网络、原生 API)
  2. 识别用户交互模式(单窗口、多窗口、托盘应用)
  3. 确定数据持久化需求(本地存储、SQLite、文件系统)
  4. 梳理集成点(外部 API、本地服务、硬件)

决策点: 你应该能够明确说明:

  • “此应用需要访问 [X] 系统资源”
  • “主要用户流程是 [Y]”
  • “安全敏感级别为 [Z]”
第 2 步:架构设计(安全优先)

目标: 设计安全的主进程/渲染进程架构。

思考框架——安全原则:

  1. 最小权限: 渲染进程应具备尽可能少的能力
  2. 纵深防御: 设置多层保护
  3. 明确通信: 所有 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 步:预加载脚本设计

目标: 在两个环境之间建立精简且安全的桥接。

思考框架:

  • “渲染进程实际需要的最少能力是什么?”
  • “我是否暴露了不必要的内容?”
  • “每个暴露的函数是否都经过验证?”

预加载设计原则:

  1. 精简接口: 只暴露绝对必要的内容
  2. 不直接暴露原始 IPC: 封装 ipcRenderer,而不是直接暴露
  3. 类型定义: 为渲染进程提供 TypeScript 类型
  4. 单向绑定: 优先使用 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:打包与分发

目标: 配置可靠的跨平台分发方案。

思考框架:

  • “目标平台有哪些?”
  • “如何发布更新?”
  • “需要哪些签名/公证?”

平台检查清单:

平台签名分发方式
macOSDeveloper ID + 公证DMG、PKG 或 Mac App Store
Windows代码签名证书NSIS、MSI 或 Microsoft Store
LinuxGPG(可选)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