desktop-framework-electron

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Electron Desktop Applications

Electron桌面应用

Quick Guide: Electron apps run two process types: a main process (Node.js, manages windows and system APIs) and renderer processes (Chromium, one per window). All communication between them flows through IPC via a preload script that uses
contextBridge
to expose a minimal, typed API surface. Never disable
contextIsolation
. Never enable
nodeIntegration
in renderers. Package with Electron Forge or Electron Builder. Auto-update via
autoUpdater
(Squirrel on macOS/Windows) or
electron-updater
for all platforms.

<critical_requirements>
快速指南: Electron应用运行两种进程类型:主进程(基于Node.js,管理窗口和系统API)和渲染进程(基于Chromium,每个窗口对应一个)。它们之间的所有通信都通过IPC进行,借助预加载脚本使用
contextBridge
暴露一个最小化的类型化API接口。永远不要禁用
contextIsolation
。永远不要在渲染进程中启用
nodeIntegration
。使用Electron Forge或Electron Builder进行打包。通过
autoUpdater
(macOS/Windows平台使用Squirrel)或
electron-updater
实现全平台自动更新。

<critical_requirements>

CRITICAL: Before Using This Skill

关键要求:使用此技能前须知

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST keep
contextIsolation: true
(the default) -- disabling it exposes the entire preload scope to untrusted renderer code)
(You MUST use
contextBridge.exposeInMainWorld()
in preload scripts -- never expose
ipcRenderer
directly)
(You MUST NOT enable
nodeIntegration: true
in any BrowserWindow -- it gives renderers full Node.js access, which is a critical security vulnerability)
(You MUST validate and sanitize ALL data received via IPC in the main process -- treat renderer messages as untrusted input)
(You MUST use
ipcMain.handle()
/
ipcRenderer.invoke()
for request-response IPC -- avoid
sendSync
which blocks the renderer)
(You MUST NOT load remote URLs with
nodeIntegration
or disabled
contextIsolation
-- this is equivalent to giving the remote site full system access)
</critical_requirements>

Auto-detection: Electron, electron, BrowserWindow, ipcMain, ipcRenderer, contextBridge, preload, webPreferences, electron-builder, electron-forge, app.whenReady, electronAPI, mainWindow, autoUpdater, nativeTheme, safeStorage, Tray, Menu, dialog, protocol, shell
When to use:
  • Building cross-platform desktop applications
  • Configuring main process / renderer process architecture
  • Setting up secure IPC communication patterns
  • Integrating with native OS features (tray, menus, dialogs, notifications, file system)
  • Packaging and distributing desktop applications
  • Implementing auto-update functionality
  • Registering custom protocol handlers / deep links
When NOT to use:
  • Choosing a UI framework for the renderer (use the appropriate web framework skill)
  • Styling the renderer UI (use the appropriate styling skill)
  • Server-side or backend logic not related to the main process
  • Mobile applications (Electron is desktop-only)
  • CLI tools that do not need a GUI

<patterns>
所有代码必须遵循CLAUDE.md中的项目规范(短横线命名法、命名导出、导入顺序、
import type
、命名常量)
(必须保持
contextIsolation: true
(默认设置)——禁用它会将整个预加载作用域暴露给不可信的渲染进程代码)
(必须在预加载脚本中使用
contextBridge.exposeInMainWorld()
——绝不能直接暴露
ipcRenderer
(绝不能在任何BrowserWindow中启用
nodeIntegration: true
——这会赋予渲染进程完整的Node.js访问权限,属于严重安全漏洞)
(必须在主进程中验证和清理所有通过IPC接收的数据——将渲染进程消息视为不可信输入)
(必须使用
ipcMain.handle()
/
ipcRenderer.invoke()
处理请求响应式IPC——避免使用会阻塞渲染进程的
sendSync
(绝不能在启用
nodeIntegration
或禁用
contextIsolation
的情况下加载远程URL——这等同于授予远程站点完整的系统访问权限)
</critical_requirements>

自动检测关键词: Electron, electron, BrowserWindow, ipcMain, ipcRenderer, contextBridge, preload, webPreferences, electron-builder, electron-forge, app.whenReady, electronAPI, mainWindow, autoUpdater, nativeTheme, safeStorage, Tray, Menu, dialog, protocol, shell
适用场景:
  • 构建跨平台桌面应用
  • 配置主进程/渲染进程架构
  • 设置安全的IPC通信模式
  • 集成原生操作系统功能(托盘、菜单、对话框、通知、文件系统)
  • 打包和分发桌面应用
  • 实现自动更新功能
  • 注册自定义协议处理器/深度链接
不适用场景:
  • 为渲染进程选择UI框架(使用对应的Web框架技能)
  • 为渲染进程UI设置样式(使用对应的样式技能)
  • 与主进程无关的服务端或后端逻辑
  • 移动应用(Electron仅适用于桌面端)
  • 不需要GUI的CLI工具

<patterns>

Key Patterns

核心模式

Pattern 1: Secure BrowserWindow Creation

模式1:安全的BrowserWindow创建

Every BrowserWindow must use a preload script and rely on the secure defaults:
contextIsolation: true
,
sandbox: true
,
nodeIntegration: false
.
javascript
const mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  webPreferences: {
    preload: path.join(__dirname, "preload.js"),
    // contextIsolation: true  -- default since Electron 12
    // sandbox: true           -- default since Electron 20
    // nodeIntegration: false  -- default since Electron 5
  },
});
Key point: Never override the security defaults. The preload script is the ONLY bridge between main and renderer. See examples/core.md.

每个BrowserWindow必须使用预加载脚本,并依赖安全默认值:
contextIsolation: true
sandbox: true
nodeIntegration: false
javascript
const mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  webPreferences: {
    preload: path.join(__dirname, "preload.js"),
    // contextIsolation: true  -- Electron 12起默认启用
    // sandbox: true           -- Electron 20起默认启用
    // nodeIntegration: false  -- Electron 5起默认启用
  },
});
核心要点: 绝不要覆盖安全默认值。预加载脚本是主进程与渲染进程之间的唯一桥梁。详见examples/core.md

Pattern 2: Preload with contextBridge

模式2:使用contextBridge的预加载脚本

The preload script exposes a narrow, explicitly typed API to the renderer. Never expose
ipcRenderer
directly.
javascript
// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  readFile: (filePath) => ipcRenderer.invoke("read-file", filePath),
  onUpdateAvailable: (callback) => {
    ipcRenderer.on("update-available", (_event, data) => callback(data));
  },
});
Key point: Each exposed method wraps a single IPC channel. The renderer calls
window.electronAPI.readFile(path)
with no knowledge of IPC internals. See examples/core.md.

预加载脚本向渲染进程暴露一个窄范围、显式类型化的API。绝不能直接暴露
ipcRenderer
javascript
// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  readFile: (filePath) => ipcRenderer.invoke("read-file", filePath),
  onUpdateAvailable: (callback) => {
    ipcRenderer.on("update-available", (_event, data) => callback(data));
  },
});
核心要点: 每个暴露的方法对应一个IPC通道。渲染进程通过
window.electronAPI.readFile(path)
调用,无需了解IPC内部细节。详见examples/core.md

Pattern 3: IPC Request-Response (invoke/handle)

模式3:IPC请求-响应模式(invoke/handle)

Use
ipcMain.handle()
in main and
ipcRenderer.invoke()
in preload for async two-way communication.
javascript
// main process
ipcMain.handle("read-file", async (_event, filePath) => {
  const content = await fs.readFile(filePath, "utf-8");
  return { success: true, content };
});
Key point:
handle
/
invoke
returns a Promise. Always validate
filePath
in the handler -- never trust renderer input. See examples/ipc.md for all IPC patterns.

在主进程中使用
ipcMain.handle()
,在预加载脚本中使用
ipcRenderer.invoke()
实现异步双向通信。
javascript
// 主进程
ipcMain.handle("read-file", async (_event, filePath) => {
  const content = await fs.readFile(filePath, "utf-8");
  return { success: true, content };
});
核心要点:
handle
/
invoke
返回Promise。必须在处理器中验证
filePath
——绝不要信任渲染进程输入。所有IPC模式详见examples/ipc.md

Pattern 4: Main-to-Renderer Messages

模式4:主进程到渲染进程的消息推送

Use
webContents.send()
from main and listen in the preload with a callback pattern.
javascript
// main: send to specific window
mainWindow.webContents.send("update-progress", { percent: 45 });

// preload: expose listener
onUpdateProgress: (callback) => {
  ipcRenderer.on("update-progress", (_event, data) => callback(data));
},
Key point: The renderer cannot pull from main -- main must push. Always scope listeners to specific channels. See examples/ipc.md.

在主进程中使用
webContents.send()
,在预加载脚本中通过回调模式监听。
javascript
// 主进程:向指定窗口发送消息
mainWindow.webContents.send("update-progress", { percent: 45 });

// 预加载脚本:暴露监听器
onUpdateProgress: (callback) => {
  ipcRenderer.on("update-progress", (_event, data) => callback(data));
},
核心要点: 渲染进程无法主动从主进程拉取数据——必须由主进程推送。始终将监听器限定在特定通道。详见examples/ipc.md

Pattern 5: App Lifecycle

模式5:应用生命周期管理

The main process manages the app lifecycle with platform-specific conventions.
javascript
app.whenReady().then(() => {
  createWindow();
  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});
Key point: macOS apps stay alive when all windows close (
window-all-closed
should not quit). The
activate
event recreates a window when the dock icon is clicked. See examples/core.md.

主进程按照平台特定的规范管理应用生命周期。
javascript
app.whenReady().then(() => {
  createWindow();
  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});
核心要点: macOS应用在所有窗口关闭后仍保持运行(
window-all-closed
事件不应触发退出)。点击dock图标时,
activate
事件会重新创建窗口。详见examples/core.md

Pattern 6: Native OS Integration

模式6:原生操作系统集成

Electron exposes native APIs for dialogs, menus, tray icons, notifications, and more -- all accessed from the main process.
javascript
const { dialog } = require("electron");

const result = await dialog.showOpenDialog(mainWindow, {
  properties: ["openFile", "multiSelections"],
  filters: [{ name: "Documents", extensions: ["txt", "md", "json"] }],
});
Key point: Native dialogs are modal to a window when passed
mainWindow
as the first argument. See examples/native-apis.md.

Electron提供了用于对话框、菜单、托盘图标、通知等功能的原生API——所有这些都从主进程访问。
javascript
const { dialog } = require("electron");

const result = await dialog.showOpenDialog(mainWindow, {
  properties: ["openFile", "multiSelections"],
  filters: [{ name: "Documents", extensions: ["txt", "md", "json"] }],
});
核心要点: 当将
mainWindow
作为第一个参数传入时,原生对话框会成为该窗口的模态窗口。详见examples/native-apis.md

Pattern 7: Custom Protocol / Deep Linking

模式7:自定义协议/深度链接

Register your app to handle
myapp://
URLs for deep linking from browsers or other apps.
javascript
if (process.defaultApp) {
  app.setAsDefaultProtocolClient("myapp", process.execPath, [
    path.resolve(process.argv[1]),
  ]);
} else {
  app.setAsDefaultProtocolClient("myapp");
}
Key point: In development (
process.defaultApp
is true), you must pass the script path as an argument. On macOS, handle the
open-url
event on the
app
object. On Windows/Linux, handle via
second-instance
event. See examples/native-apis.md.
</patterns>
<decision_framework>
注册应用以处理
myapp://
格式的URL,实现从浏览器或其他应用的深度链接。
javascript
if (process.defaultApp) {
  app.setAsDefaultProtocolClient("myapp", process.execPath, [
    path.resolve(process.argv[1]),
  ]);
} else {
  app.setAsDefaultProtocolClient("myapp");
}
核心要点: 在开发环境中(
process.defaultApp
为true),必须将脚本路径作为参数传入。在macOS上,通过
app
对象的
open-url
事件处理;在Windows/Linux上,通过
second-instance
事件处理。详见examples/native-apis.md
</patterns>
<decision_framework>

Decision Framework

决策框架

IPC Pattern Selection

IPC模式选择

Which IPC pattern?
+-- Renderer needs a response from main?
|   +-- YES --> ipcMain.handle() + ipcRenderer.invoke()
+-- Renderer sends data, no response needed?
|   +-- YES --> ipcMain.on() + ipcRenderer.send()
+-- Main needs to push data to renderer?
|   +-- YES --> webContents.send() + ipcRenderer.on() (in preload)
+-- Two renderers need to communicate?
|   +-- YES --> Route through main process (never direct renderer-to-renderer)
+-- High-frequency data transfer (streaming)?
    +-- YES --> MessageChannelMain / MessagePort pair
选择哪种IPC模式?
+-- 渲染进程需要主进程返回响应?
|   +-- 是 --> ipcMain.handle() + ipcRenderer.invoke()
+-- 渲染进程发送数据,无需响应?
|   +-- 是 --> ipcMain.on() + ipcRenderer.send()
+-- 主进程需要向渲染进程推送数据?
|   +-- 是 --> webContents.send() + ipcRenderer.on()(在预加载脚本中)
+-- 两个渲染进程需要通信?
|   +-- 是 --> 通过主进程中转(绝不能直接渲染进程间通信)
+-- 高频数据传输(流式)?
    +-- 是 --> MessageChannelMain / MessagePort 配对

Window Architecture

窗口架构选择

How many windows?
+-- Single window app?
|   +-- One BrowserWindow, one preload script
+-- Multi-window (e.g., preferences, about)?
|   +-- Separate BrowserWindow per view, each with its own preload
+-- Frameless / custom title bar?
|   +-- frame: false + custom drag regions via CSS (-webkit-app-region: drag)
+-- Persistent background work?
    +-- Use a hidden BrowserWindow or utilityProcess (Electron 22+)
</decision_framework>

Detailed resources:
  • examples/core.md - App lifecycle, BrowserWindow, preload, contextBridge fundamentals
  • examples/ipc.md - All IPC patterns: invoke/handle, send/on, main-to-renderer, MessagePort
  • examples/security.md - Security hardening, CSP, permission handlers, safe defaults
  • examples/native-apis.md - Dialogs, menus, tray, notifications, protocol handlers, auto-updater
  • examples/packaging.md - Electron Forge, Electron Builder, code signing, distribution
  • reference.md - API quick-reference tables, version history, security checklist

<red_flags>
需要多少个窗口?
+-- 单窗口应用?
|   +-- 一个BrowserWindow,一个预加载脚本
+-- 多窗口(如偏好设置、关于页面)?
|   +-- 每个视图对应单独的BrowserWindow,各有独立预加载脚本
+-- 无边框/自定义标题栏?
|   +-- frame: false + 通过CSS设置自定义拖拽区域(-webkit-app-region: drag)
+-- 持久化后台任务?
    +-- 使用隐藏的BrowserWindow或utilityProcess(Electron 22+)
</decision_framework>

详细资源:
  • examples/core.md - 应用生命周期、BrowserWindow、预加载脚本、contextBridge基础
  • examples/ipc.md - 所有IPC模式:invoke/handle、send/on、主进程到渲染进程、MessagePort
  • examples/security.md - 安全加固、CSP、权限处理器、安全默认值
  • examples/native-apis.md - 对话框、菜单、托盘、通知、协议处理器、自动更新
  • examples/packaging.md - Electron Forge、Electron Builder、代码签名、分发
  • reference.md - API速查表、版本历史、安全检查清单

<red_flags>

RED FLAGS

警示信号

Critical Security Issues:
  • Disabling
    contextIsolation
    (
    contextIsolation: false
    ) -- exposes preload globals to renderer
  • Enabling
    nodeIntegration: true
    -- gives renderer full Node.js access (fs, child_process, etc.)
  • Exposing
    ipcRenderer
    directly via
    contextBridge
    instead of wrapping individual channels
  • Loading remote/untrusted URLs without
    sandbox: true
  • Disabling
    webSecurity
    in production (
    webSecurity: false
    disables same-origin policy)
  • Not validating IPC arguments in main process handlers (path traversal, injection attacks)
  • Using
    shell.openExternal()
    with unvalidated URLs (can execute arbitrary commands)
Architecture Issues:
  • Using
    ipcRenderer.sendSync()
    -- blocks the renderer process, causes UI freezes
  • Putting business logic in the renderer instead of the main process
  • Direct renderer-to-renderer communication (bypassing main)
  • Using
    remote
    module (removed in Electron 14+, was a security and performance hazard)
  • Creating BrowserWindows from the renderer process
  • Not handling
    window-all-closed
    per-platform (macOS apps should not quit)
Packaging Issues:
  • Shipping
    devDependencies
    in production builds (bloated app size)
  • Not code-signing the application (OS warnings, auto-update failures on macOS)
  • Bundling
    node_modules
    without pruning or using ASAR archive
  • Hardcoding absolute paths that differ between dev and packaged environments
  • Using
    __dirname
    in renderer code (unavailable in sandboxed renderers)
Common Mistakes:
  • Forgetting
    app.whenReady()
    -- APIs are unavailable before the
    ready
    event
  • Not re-creating window on
    activate
    event (macOS dock click does nothing)
  • Using
    require()
    in renderer scripts loaded via
    <script>
    tags (not available in sandboxed renderers)
  • Setting
    nodeIntegrationInSubFrames: true
    for iframes loading external content
  • Not setting proper
    Content-Security-Policy
    headers for renderer HTML
</red_flags>

<critical_reminders>
严重安全问题:
  • 禁用
    contextIsolation
    contextIsolation: false
    )——将预加载全局变量暴露给渲染进程
  • 启用
    nodeIntegration: true
    ——赋予渲染进程完整的Node.js访问权限(fs、child_process等)
  • 通过
    contextBridge
    直接暴露
    ipcRenderer
    而非封装单独通道
  • 在未启用
    sandbox: true
    的情况下加载远程/不可信URL
  • 生产环境中禁用
    webSecurity
    webSecurity: false
    会关闭同源策略)
  • 主进程处理器中未验证IPC参数(路径遍历、注入攻击)
  • 使用
    shell.openExternal()
    处理未验证的URL(可能执行任意命令)
架构问题:
  • 使用
    ipcRenderer.sendSync()
    ——阻塞渲染进程,导致UI冻结
  • 将业务逻辑放在渲染进程而非主进程中
  • 渲染进程间直接通信(绕过主进程)
  • 使用
    remote
    模块(Electron 14+已移除,存在安全和性能隐患)
  • 从渲染进程创建BrowserWindow
  • 未按平台处理
    window-all-closed
    事件(macOS应用不应退出)
打包问题:
  • 生产构建中包含
    devDependencies
    (应用体积臃肿)
  • 未对应用进行代码签名(系统警告,macOS上自动更新失败)
  • 未裁剪或使用ASAR归档打包
    node_modules
  • 硬编码开发环境与打包环境不同的绝对路径
  • 在渲染进程代码中使用
    __dirname
    (沙箱化渲染进程中不可用)
常见错误:
  • 忘记
    app.whenReady()
    ——
    ready
    事件前API不可用
  • 未在
    activate
    事件中重新创建窗口(点击macOS dock图标无反应)
  • 在通过
    <script>
    标签加载的渲染进程脚本中使用
    require()
    (沙箱化渲染进程中不可用)
  • 为加载外部内容的iframe设置
    nodeIntegrationInSubFrames: true
  • 未为渲染进程HTML设置合适的
    Content-Security-Policy
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

关键提醒

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST keep
contextIsolation: true
(the default) -- disabling it exposes the entire preload scope to untrusted renderer code)
(You MUST use
contextBridge.exposeInMainWorld()
in preload scripts -- never expose
ipcRenderer
directly)
(You MUST NOT enable
nodeIntegration: true
in any BrowserWindow -- it gives renderers full Node.js access, which is a critical security vulnerability)
(You MUST validate and sanitize ALL data received via IPC in the main process -- treat renderer messages as untrusted input)
(You MUST use
ipcMain.handle()
/
ipcRenderer.invoke()
for request-response IPC -- avoid
sendSync
which blocks the renderer)
Failure to follow these rules will create severe security vulnerabilities or broken desktop applications.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目规范(短横线命名法、命名导出、导入顺序、
import type
、命名常量)
(必须保持
contextIsolation: true
(默认设置)——禁用它会将整个预加载作用域暴露给不可信的渲染进程代码)
(必须在预加载脚本中使用
contextBridge.exposeInMainWorld()
——绝不能直接暴露
ipcRenderer
(绝不能在任何BrowserWindow中启用
nodeIntegration: true
——这会赋予渲染进程完整的Node.js访问权限,属于严重安全漏洞)
(必须在主进程中验证和清理所有通过IPC接收的数据——将渲染进程消息视为不可信输入)
(必须使用
ipcMain.handle()
/
ipcRenderer.invoke()
处理请求响应式IPC——避免使用会阻塞渲染进程的
sendSync
不遵守这些规则会导致严重的安全漏洞或桌面应用故障。
</critical_reminders>