electron-dev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Electron 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

快速开始

  1. Core Rules: For detailed Electron development patterns and guidelines, refer to
    references/patterns.md
    within this skill.
  2. Security: Always enable
    contextIsolation
    ,
    sandbox
    , and disable
    nodeIntegration
    .
  3. Architecture: Main process handles Node.js operations; renderer focuses on UI; preload bridges them securely.
  1. 核心规则:如需了解详细的Electron开发模式和规范,请参考本skill内的
    references/patterns.md
    文件。
  2. 安全:始终启用
    contextIsolation
    sandbox
    ,禁用
    nodeIntegration
  3. 架构:主进程处理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
    app.isReady()
    before creating windows
  • Clean up resources on
    window.destroyed
    event
  • 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
webPreferences
with these settings:
typescript
webPreferences: {
  preload: path.join(__dirname, 'preload.js'),
  contextIsolation: true,        // REQUIRED
  enableRemoteModule: false,     // REQUIRED
  sandbox: true,                 // REQUIRED
  nodeIntegration: false,        // REQUIRED
  webSecurity: true,
}
始终按以下配置设置
webPreferences
typescript
webPreferences: {
  preload: path.join(__dirname, 'preload.js'),
  contextIsolation: true,        // REQUIRED
  enableRemoteModule: false,     // REQUIRED
  sandbox: true,                 // REQUIRED
  nodeIntegration: false,        // REQUIRED
  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
:
  1. Exclude from rollup in electron-vite config
  2. Include in
    extraResources
    in Electron Builder config
  3. Use dynamic imports in main process
  4. Rebuild for Electron using
    @electron/rebuild
对于
better-sqlite3
这类原生模块:
  1. 在electron-vite配置中将其从rollup打包中排除
  2. 在Electron Builder配置的
    extraResources
    中引入
  3. 在主进程中使用动态导入
  4. @electron/rebuild
    为Electron重新构建模块

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
    ,
    sandbox
    , no
    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 (
    pnpm run typecheck
    ) and tests (
    pnpm run test
    )
在完成涉及Electron的任务前,请确认:
  • 已启用安全配置(
    contextIsolation
    sandbox
    ,未开启
    nodeIntegration
  • IPC通道已在预加载脚本中加入白名单
  • 所有IPC入参已在主进程中完成校验
  • 原生模块已从打包工具中排除,并加入extraResources
  • 密钥仅保存在主进程中(未暴露给渲染进程)
  • 已运行类型检查(
    pnpm run typecheck
    )和测试(
    pnpm run test

Troubleshooting

故障排查

IssueSolution
White screen in productionCheck asset paths use
base: './'
, verify build output
Native module failsExclude from rollup, include in extraResources
IPC not workingVerify preload path, contextIsolation enabled, channel whitelisted
HMR not workingRun with
-w
flag, check VITE_DEV_SERVER_URL
For detailed configuration examples and additional patterns, consult
references/patterns.md
.
问题解决方案
生产环境白屏检查资源路径是否使用
base: './'
,确认构建输出是否正确
原生模块运行失败从rollup中排除,加入extraResources
IPC不工作校验预加载脚本路径、是否启用contextIsolation、通道是否加入白名单
热更新不工作带上
-w
参数运行,检查VITE_DEV_SERVER_URL配置
如需详细配置示例和更多模式,请参考
references/patterns.md