mcpField
E
electron-best-practices

electron-best-practices

github.com/ninjasln-labs开发451 次安装3 周前更新
代码仓库

关于 electron-best-practices

面向使用 React 构建 Electron 应用的开发者和 Agent,提供安全架构、类型安全 IPC、项目配置、打包签名与测试指导。还附带安全分析、项目脚手架和 IPC 类型生成脚本。

主要功能

  1. 01contextBridge 与沙箱安全模式
  2. 02类型安全的 IPC 通信模式
  3. 03electron-vite 与 Electron Forge 配置
  4. 04打包、代码签名与公证指导
  5. 05安全分析、脚手架和类型生成脚本
  6. 06Playwright 与单元测试指导

适用场景

  1. 01搭建 Electron + React 应用
  2. 02实现主进程与渲染进程通信
  3. 03检查 Electron 项目的安全配置
  4. 04准备桌面应用打包和签名
  5. 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.md
forge.config.js.md
playwright.config.ts.md
tsconfig.json.md
assets/examples/
multi-window-example.md
typed-ipc-example.md
assets/templates/
ipc-handler.ts.md
main-process.ts.md
preload-script.ts.md
react-root.tsx.md
references/architecture/
multi-window-state.md
process-separation.md
project-structure.md
references/integration/
react-patterns.md
state-management.md
references/ipc/
electron-trpc.md
error-serialization.md
typed-ipc.md
references/packaging/
auto-updates.md
bundle-optimization.md
ci-cd-patterns.md
code-signing.md
references/security/
context-isolation.md
csp-and-permissions.md
security-checklist.md
references/testing/
playwright-e2e.md
test-structure.md
unit-testing.md
references/tooling/
electron-forge.md
electron-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
IPCinvoke/handle 模式将 send/on 用于请求-响应
Preload类型化函数封装暴露原始 ipcRenderer
构建工具electron-vite基于 webpack 的工具链
打包Electron Forge手动打包
状态Zustand + electron-store简单应用使用 Redux
测试Playwright E2ESpectron(已弃用)
更新electron-updater手动检查更新
签名集成 CI 的代码签名未签名的发布版本
CSPHTTP 标头,仅 '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: trueXSS 会升级为完整的 RCE保持禁用(默认)
直接暴露 ipcRenderer渲染进程可完全访问 IPC使用 contextBridge 函数进行封装
缺少 contextIsolation渲染进程可以访问 preload 作用域保持启用(Electron 12 起默认启用)
未进行代码签名操作系统安全警告,Gatekeeper 会拦截为所有平台签名并进行公证
未启用 sandbox 的 BrowserWindowPreload 拥有完整的 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 Mode
  • references/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 应用相关指南不适用于该运行时停止操作;上文“不适用的情况”中的边界说明已列出这些运行时。

评论

支持 Markdown