关于 electron-best-practices
面向使用 React 构建 Electron 应用的开发者和 Agent,提供安全架构、类型安全 IPC、项目配置、打包签名与测试指导。还附带安全分析、项目脚手架和 IPC 类型生成脚本。
主要功能
- 01contextBridge 与沙箱安全模式
- 02类型安全的 IPC 通信模式
- 03electron-vite 与 Electron Forge 配置
- 04打包、代码签名与公证指导
- 05安全分析、脚手架和类型生成脚本
- 06Playwright 与单元测试指导
适用场景
- 01搭建 Electron + React 应用
- 02实现主进程与渲染进程通信
- 03检查 Electron 项目的安全配置
- 04准备桌面应用打包和签名
- 05为 Electron 应用编写端到端测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ninjasln-labs/ agent-skills --skill electron-best-practices安装后在 Claude Code 中输入/electron-best-practices确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/configs/electron-vite.config.ts.mdforge.config.js.mdplaywright.config.ts.mdtsconfig.json.mdassets/examples/multi-window-example.mdtyped-ipc-example.mdassets/templates/ipc-handler.ts.mdmain-process.ts.mdpreload-script.ts.mdreact-root.tsx.mdreferences/architecture/multi-window-state.mdprocess-separation.mdproject-structure.mdreferences/integration/react-patterns.mdstate-management.mdreferences/ipc/electron-trpc.mderror-serialization.mdtyped-ipc.mdreferences/packaging/auto-updates.mdbundle-optimization.mdci-cd-patterns.mdcode-signing.mdreferences/security/context-isolation.mdcsp-and-permissions.mdsecurity-checklist.mdreferences/testing/playwright-e2e.mdtest-structure.mdunit-testing.mdreferences/tooling/electron-forge.mdelectron-vite.md详细介绍
Electron + React 最佳实践
中文速览(Quick Guide)
- 做什么:给出用 React 开发 Electron 应用的安全、类型安全 IPC、进程集成、打包签名公证与测试模式,并提供分析、生成脚手架与类型的参考件。
- 何时用:编写主进程、preload 或渲染进程代码,配置 electron-vite 或 Forge,搭建 IPC,实施 contextBridge/sandbox/CSP 安全措施,为应用签名和公证,或用 Playwright 测试 Electron 应用时;不适用于 Tauri、纯 Web 应用、Electron 20 以下版本或非 React 渲染框架。
- 核心步骤:①先采用安全默认设置(contextIsolation、sandbox、关闭 nodeIntegration,主进程与渲染进程间的通信全部通过 contextBridge)②从
references/中选用模式:类型化 IPC、进程分离、React 集成 ③用electron-vite或 Forge 打包并进行代码签名/公证 ④用 Playwright 进行端到端测试,并用 Jest/Vitest 进行单元测试。 - 国内可达性:模式、参考文档与模板都在本包内(
references/、assets/templates/),无需联网即可套用;正文中的可选操作需要外部环境——GitHub Actions 矩阵构建,以及用deno run运行scripts/下的便捷脚本。若无法执行,可直接按正文所述的可观察出口处理,并说明未运行的步骤。
指导 AI Agent 使用 React 构建安全、可用于生产环境的 Electron 应用。本技能提供安全模式、类型安全的 IPC 通信、项目设置指南、打包与代码签名工作流,以及用于分析、脚手架生成和类型生成的工具。
何时使用此技能
在以下情况下使用此技能:
- 生成 Electron 主进程、preload 或渲染进程代码
- 配置 electron-vite 或 Electron Forge
- 设置进程间的 IPC 通信
- 实施安全模式(contextBridge、sandbox、CSP)
- 打包、签名并公证桌面应用
- 使用 Playwright 测试 Electron 应用
- 设计多窗口架构
以下情况请勿使用此技能:
- 构建 Tauri 应用(范式不同,请使用 Tauri 专用指南)
- 构建没有桌面需求的纯 Web 应用
- 针对低于 20 的 Electron 版本(安全默认设置有所不同)
- 使用非 React 渲染框架(请使用相应框架专用技能)
核心原则
1. 安全优先的架构
现代 Electron 安全依赖于三项默认设置,这些设置从 Electron 20+ 起成为标准:上下文隔离、沙箱模式,以及禁用 nodeIntegration。禁用其中任何一项,都可能使 XSS 攻击升级为完整的远程代码执行。主进程与渲染进程间的所有通信都必须通过 contextBridge:
// preload.ts - SECURE pattern
contextBridge.exposeInMainWorld('electronAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs'),
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
onUpdateCounter: (callback: (value: number) => void) => {
const handler = (_event: IpcRendererEvent, value: number) => callback(value);
ipcRenderer.on('update-counter', handler);
return () => ipcRenderer.removeListener('update-counter', handler);
}
});
对于加载本地文件的应用,应通过 HTTP 标头设置内容安全策略,将脚本来源限制为 'self'。
2. 类型安全的 IPC 通信
对于请求-响应通信,推荐使用 invoke/handle 模式,而非 send/on;它能提供恰当的 async/await 语义和错误传播。对于类型化通道,请使用映射类型模式:
type IpcChannelMap = {
'load-prefs': { args: []; return: UserPreferences };
'save-file': { args: [content: string]; return: { success: boolean } };
};
对于复杂应用,electron-trpc 使用 tRPC 的路由器模式和 Zod 验证,可提供完整的类型安全:
export const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello, ${input.name}!`),
});
处理跨越 IPC 边界的错误时需格外注意,因为 Electron 只会序列化 Error 对象的 message 属性。请将响应封装为 { success, data, error } 结果类型,以保留完整的错误上下文。
3. 现代项目设置
推荐的技术栈使用 electron-vite 进行开发,并使用 Electron Forge 进行打包。electron-vite 提供统一配置来管理主进程、preload 和渲染进程,开发服务器启动时间不到 1 秒,并支持即时 HMR。Electron Forge 使用 Electron 官方的一方软件包进行签名和公证。
src/
├── main/ # Main process (Node.js environment)
│ ├── index.ts
│ └── ipc/ # IPC handlers
├── preload/ # Secure bridge via contextBridge
│ ├── index.ts
│ └── index.d.ts # TypeScript declarations for exposed APIs
└── renderer/ # React application (pure web, no Node access)
├── src/
└── index.html
4. React 集成模式
React 18 的并发特性在基于 Chromium 的 Electron 渲染进程中可正常运行。Strict Mode 对副作用的双重调用可以发现 IPC 监听器泄漏,否则这类泄漏会导致内存问题。注册 IPC 监听器的副作用始终应返回清理函数:
useEffect(() => {
const cleanup = window.electronAPI.onUpdateCounter((value) => {
setCount(value);
});
return cleanup;
}, []);
对于多窗口应用,主进程应作为共享状态的唯一事实来源。使用 electron-store 持久化数据,并结合 IPC 广播,使任一窗口中的状态变更都能更新其他所有窗口。
快速参考
| 类别 | 优先选择 | 避免 |
|---|---|---|
| 安全性 | contextBridge.exposeInMainWorld() | nodeIntegration: true |
| IPC | invoke/handle 模式 | 将 send/on 用于请求-响应 |
| Preload | 类型化函数封装 | 暴露原始 ipcRenderer |
| 构建工具 | electron-vite | 基于 webpack 的工具链 |
| 打包 | Electron Forge | 手动打包 |
| 状态 | Zustand + electron-store | 简单应用使用 Redux |
| 测试 | Playwright E2E | Spectron(已弃用) |
| 更新 | electron-updater | 手动检查更新 |
| 签名 | 集成 CI 的代码签名 | 未签名的发布版本 |
| CSP | HTTP 标头,仅 'self' | 不设置 CSP |
| 错误处理 | Result 类型 {success, data, error} | 通过 IPC 传递原始 Error |
| 多窗口 | 主进程作为状态中心 | 窗口之间直接通信 |
代码生成指南
生成 Electron 代码时,请遵循以下模式:
BrowserWindow 创建
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
contextIsolation: true,
sandbox: true,
nodeIntegration: false,
},
});
始终启用 contextIsolation 和 sandbox。切勿启用 nodeIntegration。preload 路径必须解析到构建输出位置。
IPC 处理程序模块
export function registerFileHandlers(): void {
ipcMain.handle('save-file', async (_event, content: string) => {
try {
await fs.writeFile(filePath, content);
return { success: true, data: filePath };
} catch (err) {
return { success: false, error: (err as Error).message };
}
});
}
将相关处理程序分组到模块中。所有返回值都使用 Result 类型模式。验证从渲染进程接收的所有参数。
常见反模式
生成 Electron 代码时,请避免以下模式:
| 反模式 | 问题 | 解决方案 |
|---|---|---|
nodeIntegration: true | XSS 会升级为完整的 RCE | 保持禁用(默认) |
直接暴露 ipcRenderer | 渲染进程可完全访问 IPC | 使用 contextBridge 函数进行封装 |
缺少 contextIsolation | 渲染进程可以访问 preload 作用域 | 保持启用(Electron 12 起默认启用) |
| 未进行代码签名 | 操作系统安全警告,Gatekeeper 会拦截 | 为所有平台签名并进行公证 |
未启用 sandbox 的 BrowserWindow | Preload 拥有完整的 Node.js 访问权限 | 启用 sandbox(Electron 20 起默认启用) |
| IPC 参数未经验证 | 来自渲染进程的注入攻击 | 使用 Zod 或手动检查进行验证 |
服务器绑定到 0.0.0.0 | 本地服务器暴露在网络中 | 始终绑定到 127.0.0.1 |
| 缺少 CSP 标头 | 存在脚本注入风险 | 通过 HTTP 标头设置严格的 CSP |
| 未序列化 IPC 错误 | 错误上下文无法跨边界传递 | 使用 Result 类型模式 |
| 使用 Spectron 进行测试 | 已弃用,最多支持 Electron 13 | 使用 Playwright |
完整的安全审计清单请参见 references/security/security-checklist.md。
脚本参考
analyze-security.ts
分析 Electron 项目中的安全配置错误:
deno run --allow-read scripts/analyze-security.ts <path> [options]
Options:
--strict Enable all checks
--json Output JSON for CI
-h, --help Show help
Examples:
# Analyze a project
deno run --allow-read scripts/analyze-security.ts ./src
# Strict mode for CI pipeline
deno run --allow-read scripts/analyze-security.ts ./src --strict --json
scaffold-electron-app.ts
使用安全默认配置搭建新的 Electron + React 项目:
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts [options]
Options:
--name <name> App name (required)
--path <path> Target directory (default: ./)
--with-react Include React setup
--with-trpc Include electron-trpc
--with-tests Include Playwright tests
Examples:
# Basic app with React
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts \
--name "my-app" --with-react
# Full setup with trpc and tests
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts \
--name "my-app" --with-react --with-trpc --with-tests
generate-ipc-types.ts
从 IPC 处理程序文件生成 TypeScript 类型定义:
deno run --allow-read --allow-write scripts/generate-ipc-types.ts [options]
Options:
--handlers <path> Path to IPC handler files
--output <path> Output path for type definitions
--validate Validate existing types match handlers
Examples:
# Generate types from handlers
deno run --allow-read --allow-write scripts/generate-ipc-types.ts \
--handlers ./src/main/ipc --output ./src/preload/ipc-types.d.ts
# Validate types in CI
deno run --allow-read scripts/generate-ipc-types.ts \
--handlers ./src/main/ipc --validate
更多资源
安全性
references/security/context-isolation.md- contextBridge 和隔离模式references/security/csp-and-permissions.md- 内容安全策略配置references/security/security-checklist.md- 完整的安全审计清单
IPC 通信
references/ipc/typed-ipc.md- 类型化通道映射模式references/ipc/electron-trpc.md- 用于实现完整类型安全的 tRPC 集成references/ipc/error-serialization.md- 跨 IPC 边界传递 Result 类型
架构
references/architecture/project-structure.md- 目录组织references/architecture/process-separation.md- 主进程、preload 和渲染进程的职责references/architecture/multi-window-state.md- 窗口间共享状态
React 集成
references/integration/react-patterns.md- useEffect 清理、Strict Modereferences/integration/state-management.md- Zustand 和 electron-store 模式
打包与分发
references/packaging/code-signing.md- 特定平台的签名工作流references/packaging/auto-updates.md- electron-updater 配置references/packaging/bundle-optimization.md- 缩减体积的技巧references/packaging/ci-cd-patterns.md- GitHub Actions 矩阵构建
测试
references/testing/playwright-e2e.md- Playwright 对 Electron 的支持references/testing/unit-testing.md- Jest/Vitest 多项目配置references/testing/test-structure.md- 测试组织模式
工具链
references/tooling/electron-vite.md- 构建工具配置references/tooling/electron-forge.md- 打包与分发references/tooling/tauri-comparison.md- 何时选择 Tauri
模板
assets/templates/main-process.ts.md- 主进程入门模板assets/templates/preload-script.ts.md- 使用 contextBridge 的 preload 脚本assets/templates/ipc-handler.ts.md- IPC 处理器模块模板assets/templates/react-root.tsx.md- React 根组件模板
配置示例
assets/configs/electron-vite.config.ts.md- electron-vite 配置assets/configs/forge.config.js.md- Electron Forge 配置assets/configs/tsconfig.json.md- TypeScript 配置预设assets/configs/playwright.config.ts.md- Playwright Electron 测试配置
完整示例
assets/examples/typed-ipc-example.md- 端到端类型化 IPC 演练assets/examples/multi-window-example.md- 多窗口状态管理
最简实践示例
前提条件: 一个采用上述安全默认设置的 Electron + React 应用(contextIsolation: true、sandbox: true,通过 contextBridge 使用 preload)。
调用:“实现从渲染进程保存文件,并在出错时向用户显示错误,而不是将错误吞掉。”
结果摘录(主进程注册 → preload 暴露 → 渲染进程调用并呈现错误状态):
// src/main/ipc/save-file.ts — main process registers the handler
ipcMain.handle('save-file', async (_event, content: string) => {
try {
await fs.writeFile(filePath, content);
return { success: true, data: filePath };
} catch (err) {
return { success: false, error: (err as Error).message };
}
});
// src/preload/index.ts — preload exposes exactly this one function
contextBridge.exposeInMainWorld('electronAPI', {
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
});
// renderer — invoke, then map the Result type to a user-visible state
async function handleSave(content: string) {
const result = await window.electronAPI.saveFile(content);
if (!result.success) {
setStatus({ kind: 'error', message: `Couldn't save file: ${result.error}` });
return;
}
setStatus({ kind: 'saved', path: result.data });
}
处理器绝不会让拒绝以原始 Error 的形式逸出——Result 类型会跨越边界,渲染进程则会按照下文的“向用户呈现错误”将 error 映射为可见状态。
向用户呈现错误
Result 类型模式适用于 IPC 边界;渲染进程仍需显示用户可以采取行动的信息。生成代码时:
- 将每种 IPC 失败映射为用户可见的状态:触发控件附近的内联消息(表单)、Toast 或横幅(后台操作)、错误边界(渲染崩溃)——绝不吞掉被拒绝的 Promise。
- 显示原因和后续步骤,而不只是“发生了错误”:例如
Couldn't save file: disk is full. Free up space or choose another location.。包含 Result 类型中的错误message(它正是为此目的跨越边界),但绝不显示原始堆栈跟踪。 - 区分可重试错误和致命错误:网络或离线故障应提供“重试”操作;权限和验证错误应提供说明,而不是重试按钮。
- 将完整上下文(通道名称、参数形状)记录到主进程日志中;界面上只显示摘要信息。
脚本失败退出
所有三个捆绑脚本都在 Deno 下运行。每种失败都有可观察到的症状和退出状态:
| 症状 | 原因 | 操作 |
|---|---|---|
command not found: deno | 未安装 Deno | 安装 Deno(参见 deno.com),或手动继续:手动应用 references/ 和 assets/templates/ 中的模式——这些脚本只是便利工具,并非必需项。不要默默跳过用户要求的脚手架生成或分析;应告知用户未执行哪一步。 |
读取路径时出现 PermissionDenied | 缺少 --allow-read(脚手架生成或类型生成时还需 --allow-write) | 使用上文脚本参考中列出的标志重新运行——这些是文档说明的最低要求。 |
analyze-security.ts 使用 --strict --json 时以非零状态退出 | 检查未通过(这是报告,不是崩溃) | 将 JSON 输出视为需要修复的问题,而非环境错误。发生崩溃时会打印堆栈跟踪,而不是 JSON 报告。 |
generate-ipc-types.ts --validate 以非零状态退出 | 现有类型定义与处理程序不再匹配 | 重新生成类型,然后再次运行 --validate,确认退出状态为 0。 |
scaffold-electron-app.ts 拒绝写入 | 目标目录非空,或缺少 --name | 提供必需的 --name,或将 --path 指向空目录;该脚本不会覆盖现有文件。 |
| 运行时:应用发出的网络请求失败(超时/离线,在主进程日志中可见) | 暂时性故障或主机无法访问 | 在有限次数内重试(例如,退避重试 2-3 次),然后按照“向用户显示错误”将应用降级为用户可见的错误状态,并提供“重试”操作——绝不要在静默循环中无限重试。 |
错误做法 → 修复方案(常见问题)
| 错误做法 | 可观察到的症状 | 修复方案 |
|---|---|---|
“只为一个窗口”启用 nodeIntegration | 渲染进程可以调用 require()——安全检查清单会将其标记出来 | 保持禁用状态;通过 contextBridge 仅暴露所需的函数。 |
对请求-响应调用使用 send/on | 回复容易发生竞态,且没有可等待的结果 | 使用 invoke/handle。 |
通过 IPC 传递 Error 对象 | 渲染进程收到类似 {} 的对象,其中没有消息 | 返回 Result 类型 { success, data, error }。 |
在没有 CSP 的情况下,在 BrowserWindow 中加载远程 URL | 内容安全策略检查清单项未通过 | 通过 HTTP 标头设置严格的 CSP;本地内容仅允许 'self'。 |
| 使用 Spectron 测试 | 已弃用,支持到 Electron 13 为止 | 使用 Playwright(references/testing/playwright-e2e.md)。 |
| 将此技能用于 Tauri 或纯 Web 应用 | 相关指南不适用于该运行时 | 停止操作;上文“不适用的情况”中的边界说明已列出这些运行时。 |