electron-dev
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseElectron Development Guide
Electron开发指南
This skill provides guidelines, patterns, and best practices for building production-grade Electron applications using Electron Vite and Electron Builder.
本skill提供了使用Electron Vite和Electron Builder构建生产级Electron应用的规范、模式和最佳实践。
Quick Start
快速开始
- Core Rules: For detailed Electron development patterns and guidelines, refer to within this skill.
references/patterns.md - Security: Always enable ,
contextIsolation, and disablesandbox.nodeIntegration - Architecture: Main process handles Node.js operations; renderer focuses on UI; preload bridges them securely.
- 核心规则:有关详细的Electron开发模式和规范,请参考本skill中的。
references/patterns.md - 安全:始终开启、
contextIsolation,禁用sandbox。nodeIntegration - 架构:主进程处理Node.js操作,渲染进程聚焦UI,预加载脚本作为两者之间的安全桥梁。
Process Architecture
进程架构
Main Process (Node.js Context)
主进程(Node.js上下文)
The main process handles:
- Window creation and lifecycle
- File system and database operations
- IPC message handling
- Application-level events
Key guidelines:
- Use before creating windows
app.isReady() - Clean up resources on event
window.destroyed - Handle graceful shutdown with
app.on('before-quit')
主进程负责处理:
- 窗口创建和生命周期管理
- 文件系统和数据库操作
- IPC消息处理
- 应用级事件
关键规范:
- 创建窗口前先调用判断应用是否就绪
app.isReady() - 在事件触发时清理资源
window.destroyed - 通过实现优雅关闭
app.on('before-quit')
Renderer Process (Frontend Context)
渲染进程(前端上下文)
The renderer process:
- Renders UI using web technologies (React, Vue, etc.)
- Communicates with main process via IPC only
- Never has direct access to Node.js APIs
渲染进程的职责:
- 使用Web技术(React、Vue等)渲染UI
- 仅通过IPC与主进程通信
- 永远不能直接访问Node.js API
Preload Script (Security Layer)
预加载脚本(安全层)
The preload script is the only bridge between renderer and main:
typescript
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("electronAPI", {
invoke: (channel: string, args?: unknown) => {
const validChannels = ["db:query", "file:save", "app:close"];
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
throw new Error(`Invalid IPC channel: ${channel}`);
},
});预加载脚本是渲染进程和主进程之间唯一的桥梁:
typescript
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("electronAPI", {
invoke: (channel: string, args?: unknown) => {
const validChannels = ["db:query", "file:save", "app:close"];
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
throw new Error(`Invalid IPC channel: ${channel}`);
},
});Security Checklist
安全检查清单
Always configure with these settings:
webPreferencestypescript
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // REQUIRED
enableRemoteModule: false, // REQUIRED
sandbox: true, // REQUIRED
nodeIntegration: false, // REQUIRED
webSecurity: true,
}始终按以下配置设置:
webPreferencestypescript
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 必填项
enableRemoteModule: false, // 必填项
sandbox: true, // 必填项
nodeIntegration: false, // 必填项
webSecurity: true,
}IPC Communication
IPC通信
Main Process Handler
主进程处理器
typescript
ipcMain.handle("db:insert", async (event, data: unknown) => {
if (!isValidInsertData(data)) {
throw new Error("Invalid data");
}
return db.insert(data);
});typescript
ipcMain.handle("db:insert", async (event, data: unknown) => {
if (!isValidInsertData(data)) {
throw new Error("Invalid data");
}
return db.insert(data);
});Security Rules
安全规则
- Whitelist all IPC channels in preload script
- Validate all arguments in main process handlers
- Never trust user input from renderer
- Keep secrets in main process only
- 在预加载脚本中对所有IPC通道设置白名单
- 在主进程处理器中验证所有传入参数
- 永远不信任来自渲染进程的用户输入
- 敏感密钥仅保存在主进程中
Configuration
配置
electron-vite.config.ts
electron-vite.config.ts
typescript
import { defineConfig } from "electron-vite";
export default defineConfig({
main: {
build: {
outDir: "dist/main",
rollupOptions: {
external: ["better-sqlite3", "native-module-name"],
},
},
},
preload: {
build: { outDir: "dist/preload" },
},
renderer: {
build: {
outDir: "dist/renderer",
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
},
},
},
},
},
});typescript
import { defineConfig } from "electron-vite";
export default defineConfig({
main: {
build: {
outDir: "dist/main",
rollupOptions: {
external: ["better-sqlite3", "native-module-name"],
},
},
},
preload: {
build: { outDir: "dist/preload" },
},
renderer: {
build: {
outDir: "dist/renderer",
rollupOptions: {
output: {
manualChunks: {
vendor: ["react", "react-dom"],
},
},
},
},
},
});Native Modules
原生模块
For native modules like :
better-sqlite3- Exclude from rollup in electron-vite config
- Include in in Electron Builder config
extraResources - Use dynamic imports in main process
- Rebuild for Electron using
@electron/rebuild
对于等原生模块:
better-sqlite3- 在electron-vite配置中从rollup打包列表中排除
- 在Electron Builder配置中加入
extraResources - 在主进程中使用动态导入
- 使用为Electron重新构建模块
@electron/rebuild
Common Pitfalls
常见陷阱
Never Do This
禁止做法
typescript
// Exposing entire Node.js API
contextBridge.exposeInMainWorld("require", require);
// Not validating IPC messages
ipcMain.handle("dangerous", (event, data) => {
fs.writeFileSync(data.path, data.content);
});
// Using __dirname in renderer
const imagePath = `${__dirname}/images/logo.png`;typescript
// 暴露整个Node.js API
contextBridge.exposeInMainWorld("require", require);
// 不验证IPC消息
ipcMain.handle("dangerous", (event, data) => {
fs.writeFileSync(data.path, data.content);
});
// 在渲染进程中使用__dirname
const imagePath = `${__dirname}/images/logo.png`;Do This Instead
推荐做法
typescript
// Whitelist specific channels
contextBridge.exposeInMainWorld("electronAPI", {
invoke: (channel: string, args: unknown) => {
if (!["safe:channel"].includes(channel)) throw new Error("Invalid");
return ipcRenderer.invoke(channel, args);
},
});
// Validate all arguments
ipcMain.handle("file:write", async (event, { path, content }) => {
if (typeof path !== "string" || typeof content !== "string") {
throw new TypeError("Invalid arguments");
}
// Safe to use
});typescript
// 对特定通道设置白名单
contextBridge.exposeInMainWorld("electronAPI", {
invoke: (channel: string, args: unknown) => {
if (!["safe:channel"].includes(channel)) throw new Error("Invalid");
return ipcRenderer.invoke(channel, args);
},
});
// 验证所有参数
ipcMain.handle("file:write", async (event, { path, content }) => {
if (typeof path !== "string" || typeof content !== "string") {
throw new TypeError("Invalid arguments");
}
// 可安全使用
});Validation Checklist
交付检查清单
Before finishing a task involving Electron:
- Security settings enabled (,
contextIsolation, nosandbox)nodeIntegration - IPC channels whitelisted in preload script
- All IPC arguments validated in main process
- Native modules excluded from bundler and included in extraResources
- Secrets kept in main process only (not exposed to renderer)
- Run type checks () and tests (
pnpm run typecheck)pnpm run test
完成涉及Electron的开发任务前:
- 已启用安全设置(、
contextIsolation、未开启sandbox)nodeIntegration - IPC通道已在预加载脚本中加入白名单
- 所有IPC参数已在主进程中完成验证
- 原生模块已从打包工具中排除并加入extraResources
- 敏感密钥仅保存在主进程中(未暴露给渲染进程)
- 运行类型检查()和测试(
pnpm run typecheck)pnpm run test
Troubleshooting
故障排查
| Issue | Solution |
|---|---|
| White screen in production | Check asset paths use |
| Native module fails | Exclude from rollup, include in extraResources |
| IPC not working | Verify preload path, contextIsolation enabled, channel whitelisted |
| HMR not working | Run with |
For detailed configuration examples and additional patterns, consult .
references/patterns.md| 问题 | 解决方案 |
|---|---|
| 生产环境白屏 | 检查资源路径是否配置 |
| 原生模块加载失败 | 从rollup打包列表排除,加入extraResources |
| IPC通信失效 | 验证预加载路径、contextIsolation已开启、通道已加入白名单 |
| HMR热更新不生效 | 带 |
如需详细配置示例和更多开发模式,请查阅。
references/patterns.md