关于 electron-scaffold
一项用于搭建或改造 Electron 桌面应用的技能,指导选择构建工具与前端框架,并涵盖安全、IPC、本机界面、开发和打包实践。
主要功能
- 01选择构建工具与前端框架
- 02配置安全的 Electron 与 IPC 模式
- 03设置开发及构建工作流
- 04指导本机界面与应用打包
适用场景
- 01启动新的 Electron 桌面应用
- 02改造现有 Electron 项目
- 03准备跨平台应用的打包
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chrisvoncsefalvay/ claude-skills --skill electron-scaffold安装后在 Claude Code 中输入/electron-scaffold确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/build-config.mdelectron-security.mdipc-patterns.mdscripts/scaffold.sh详细介绍
Electron 应用脚手架搭建
从一开始就内置安全性、性能和原生平台集成方面的最佳实践,创建可用于生产环境的 Electron 应用。
何时使用此技能
在以下情况下使用此技能:
- 用户想要创建新的 Electron 桌面应用
- 用户需要按照现代最佳实践搭建 Electron 项目
- 用户想要外观原生的跨平台桌面应用
- 用户提到“Electron 应用”“桌面应用”“跨平台应用”等类似说法
- 用户想要更新现有 Electron 项目的结构
前置条件检查
搭建脚手架前,请确认:
-
Node.js:检查 Node.js 版本(建议使用 18.x 或更高版本)
node --version -
npm 或 yarn:确认包管理器可用
npm --version -
Git:确保 Git 可用于版本控制
git --version
架构决策要点
1. 构建工具选择
询问用户偏好的构建系统(或根据使用场景给出建议):
Electron Forge(大多数项目的推荐选择)
- 一体化工具解决方案
- 内置 TypeScript 支持
- 易于使用的插件系统
- 适用于:大多数新项目,以及希望开箱即用的团队
Electron Builder
- 高度可配置
- 出色的多平台打包能力
- 支持自动更新
- 适用于:复杂的构建需求、特定的打包需求
Vite + Electron
- 最快的开发体验
- 现代化、以 ESM 为先的方案
- 热模块替换
- 适用于:现代 Web 框架(React、Vue、Svelte)以及注重开发速度的场景
2. 前端框架
确定 UI 框架:
- 原生 JS/TypeScript:最轻量,完全掌控
- React:最受欢迎,生态系统庞大
- Vue:渐进式,易于学习
- Svelte:包体积最小,编译时框架
- Angular:适用于企业,具有明确的设计主张
3. TypeScript 还是 JavaScript
强烈建议使用 TypeScript,以便:
- 获得更好的 IDE 支持和自动补全
- 在编译时捕获错误
- 提高可维护性
- 获得 Electron API 类型支持
工作流
第 1 步:初始化项目
根据所选工具初始化项目:
使用 Electron Forge(推荐):
npm init electron-app@latest <app-name> -- --template=webpack-typescript
使用 Vite + Electron:
npm create @quick-start/electron <app-name>
使用自定义方案: 创建 package.json 并添加适当的依赖项(参见模板)。
第 2 步:设置项目结构
创建条理清晰的项目结构:
<app-name>/
├── src/
│ ├── main/ # Main process
│ │ ├── main.ts # Entry point
│ │ ├── ipc/ # IPC handlers
│ │ ├── menu.ts # Native menu
│ │ └── tray.ts # System tray (if needed)
│ ├── preload/ # Preload scripts
│ │ └── preload.ts # Context bridge
│ ├── renderer/ # Renderer process
│ │ ├── index.html
│ │ ├── index.ts
│ │ └── styles/
│ └── shared/ # Shared types/utilities
│ └── types.ts
├── assets/ # Icons, images
├── resources/ # Build resources
├── dist/ # Build output
├── package.json
├── tsconfig.json
└── electron-builder.yml # or forge.config.js
第 3 步:安全配置
重要:从一开始就实施安全最佳实践。
1. BrowserWindow 安全选项:
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
// Security: Use preload scripts instead of nodeIntegration
nodeIntegration: false,
// Security: Isolate context between web content and preload
contextIsolation: true,
// Security: Disable remote module
enableRemoteModule: false,
// Security: Use sandboxed renderer
sandbox: true,
// Preload script for safe IPC
preload: path.join(__dirname, 'preload.js'),
// Security: Disable web security only in development if needed
webSecurity: true,
// Security: Disable navigation
allowRunningInsecureContent: false,
},
});
2. 内容安全策略(CSP):
// In main process or HTML meta tag
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"default-src 'self'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"connect-src 'self'",
].join('; '),
},
});
});
3. 上下文桥接(preload.ts):
import { contextBridge, ipcRenderer } from 'electron';
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electronAPI', {
// Invoke pattern (request-response)
getAppVersion: () => ipcRenderer.invoke('app:get-version'),
// Send pattern (one-way)
logMessage: (message: string) => ipcRenderer.send('log:message', message),
// Listener pattern (for receiving events)
onUpdateAvailable: (callback: (info: any) => void) => {
ipcRenderer.on('update:available', (_event, info) => callback(info));
},
// Remove listener
removeUpdateListener: () => {
ipcRenderer.removeAllListeners('update:available');
},
});
4. IPC 安全模式:
// main/ipc/handlers.ts
import { ipcMain } from 'electron';
// Use invoke/handle pattern for request-response
ipcMain.handle('app:get-version', async () => {
return app.getVersion();
});
// Validate and sanitize all inputs
ipcMain.handle('file:read', async (_event, filePath: string) => {
// Validate path is within allowed directories
const allowedDir = app.getPath('userData');
const resolvedPath = path.resolve(filePath);
if (!resolvedPath.startsWith(allowedDir)) {
throw new Error('Access denied');
}
return fs.readFile(resolvedPath, 'utf-8');
});
第 4 步:原生 UI 元素
创建外观原生的 UI 组件:
1. 应用菜单:
// main/menu.ts
import { Menu, shell } from 'electron';
export function createApplicationMenu(mainWindow: BrowserWindow) {
const template: MenuItemConstructorOptions[] = [
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => mainWindow.webContents.send('file:new'),
},
{ type: 'separator' },
{ role: 'quit' },
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
{
label: 'Help',
submenu: [
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org');
},
},
],
},
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
2. 系统托盘(可选):
// main/tray.ts
import { Tray, Menu, nativeImage } from 'electron';
export function createTray(mainWindow: BrowserWindow) {
const icon = nativeImage.createFromPath(
path.join(__dirname, '../assets/tray-icon.png')
);
const tray = new Tray(icon);
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show App',
click: () => {
mainWindow.show();
},
},
{
label: 'Quit',
click: () => {
app.quit();
},
},
]);
tray.setContextMenu(contextMenu);
tray.setToolTip('My Electron App');
return tray;
}
第 5 步:设置开发环境
1. 热重载配置:
// main.ts
const isDevelopment = process.env.NODE_ENV === 'development';
if (isDevelopment) {
mainWindow.loadURL('http://localhost:5173'); // Vite dev server
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
}
2. package.json 脚本:
{
"scripts": {
"dev": "concurrently \"npm:dev:*\"",
"dev:vite": "vite",
"dev:electron": "electron .",
"build": "npm run build:renderer && npm run build:main",
"build:renderer": "vite build",
"build:main": "tsc -p tsconfig.main.json",
"package": "electron-builder",
"package:all": "electron-builder -mwl",
"lint": "eslint src --ext .ts,.tsx",
"typecheck": "tsc --noEmit"
}
}
第 6 步:自动更新配置
使用 electron-updater 实现自动更新:
1. 安装依赖项:
npm install electron-updater
2. 更新配置:
// main/updater.ts
import { autoUpdater } from 'electron-updater';
export function setupAutoUpdater(mainWindow: BrowserWindow) {
// Configure update server
autoUpdater.setFeedURL({
provider: 'github',
owner: 'your-username',
repo: 'your-repo',
});
// Check for updates on startup
autoUpdater.checkForUpdatesAndNotify();
// Update events
autoUpdater.on('update-available', (info) => {
mainWindow.webContents.send('update:available', info);
});
autoUpdater.on('update-downloaded', (info) => {
mainWindow.webContents.send('update:downloaded', info);
});
autoUpdater.on('error', (err) => {
mainWindow.webContents.send('update:error', err);
});
}
3. electron-builder 配置:
# electron-builder.yml
appId: com.yourcompany.yourapp
productName: YourApp
directories:
output: dist
buildResources: resources
files:
- src/**/*
- package.json
mac:
category: public.app-category.productivity
target:
- dmg
- zip
hardenedRuntime: true
gatekeeperAssess: false
entitlements: resources/entitlements.mac.plist
win:
target:
- nsis
- portable
publisherName: Your Company
linux:
target:
- AppImage
- deb
category: Utility
publish:
provider: github
owner: your-username
repo: your-repo
第 7 步:TypeScript 配置
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src",
"types": ["node", "electron"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
第 8 步:构建和打包
开发环境:
npm run dev
生产构建:
npm run build
npm run package
多平台构建:
npm run package:all
最佳实践检查清单
搭建脚手架时,请确保实现以下内容:
安全性
- 已启用上下文隔离
- 已在渲染进程中禁用 Node 集成
- 使用上下文桥接的预加载脚本
- 已配置内容安全策略
- 对所有 IPC 处理程序进行输入验证
- 已启用沙箱模式
- 已启用 Web 安全
- 已设置导航和重定向防护
性能
- 对渲染进程模块进行延迟加载
- 已配置后台节流
- 对大型数据集进行内存管理
- 使用高效的 IPC 模式(批量更新)
- 优化 Webpack/Vite
用户体验
- 原生应用菜单
- 键盘快捷键(加速键)
- 窗口状态持久化
- 完整的图标集(所有尺寸)
- 启动画面(可选)
- 错误边界
- 加载状态
开发体验
- 已配置 TypeScript
- 热重载正常工作
- 开发环境中可使用 DevTools
- 已配置 ESLint 和 Prettier
- 使用 Husky 配置 Git 钩子(可选)
- 已启用源映射
分发
- 已配置自动更新
- 已配置代码签名(因平台而异)
- 已为所有平台配置构建脚本
- 完整的应用元数据
- 已包含许可证文件
错误处理模式
主进程错误
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Log to file or error tracking service
app.quit();
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
});
渲染进程错误
window.addEventListener('error', (event) => {
window.electronAPI.logError({
message: event.error.message,
stack: event.error.stack,
});
});
window.addEventListener('unhandledrejection', (event) => {
window.electronAPI.logError({
message: 'Unhandled Promise Rejection',
reason: event.reason,
});
});
平台特定注意事项
macOS
- 使用
.icns图标格式 - 实现 Dock 菜单
- 处理
activate事件(重新打开窗口) - 考虑 macOS 特定菜单项(关于、偏好设置)
- 分发时需要代码签名
Windows
- 使用
.ico图标格式 - 处理 Squirrel 启动事件
- 考虑 Windows Toast 通知
- 自定义 NSIS 安装程序
- 使用证书进行代码签名
Linux
- 使用
.png图标格式 - 提供
.desktop文件 - 处理不同的软件包格式(deb、AppImage、snap)
- 在多个发行版上测试
示例脚手架
最简 TypeScript 配置
# Initialize with Forge
npm init electron-app@latest my-app -- --template=webpack-typescript
# Add security defaults
# Add IPC patterns
# Configure build
React + TypeScript + Vite
# Create with Vite template
npm create @quick-start/electron my-app -- --template react-ts
# Add security hardening
# Configure auto-update
# Add native menus
可用于生产环境的完整配置
- 完整的安全配置
- 通过 GitHub Releases 自动更新
- 原生 UI 元素(菜单、托盘)
- 错误跟踪
- 分析(可选)
- 崩溃报告
- 多平台构建流水线
成功提示
- 从一开始就确保安全:不要等到之后才添加安全措施——从第一天起就将其纳入设计
- 为所有内容添加类型:在主进程和渲染进程中都使用 TypeScript
- 尽早测试 IPC:尽早发现的 IPC 问题更容易调试
- 进行平台测试:定期在所有目标平台上测试
- 监控包大小:确保渲染进程的包经过优化
- 记录 IPC 契约:维护进程间的 API 文档
- 版本控制:在 Git 中忽略
dist/、node_modules/、.env - 使用进程管理器:妥善处理主进程崩溃
- 实现日志记录:结构化日志有助于调试生产环境中的问题
- 规划更新:在首次发布前设计更新策略
应避免的常见问题
- ❌ 无充分理由启用
nodeIntegration - ❌ 跳过上下文隔离
- ❌ 未经验证就加载远程内容
- ❌ 向网页内容暴露整个 IPC 渲染器
- ❌ 忽略控制台中的安全警告
- ❌ 未在所有目标平台上进行测试
- ❌ 将文件路径硬编码(使用
app.getPath()) - ❌ 忘记处理窗口状态持久化
- ❌ 未实现恰当的错误边界
- ❌ 分发时跳过代码签名
快速入门命令
对于大多数用户,建议使用以下命令:
# Create app with TypeScript and Webpack
npm init electron-app@latest <app-name> -- --template=webpack-typescript
# Then apply security hardening, native UI, and build configuration
参考文件
如需详细的 Electron API 示例和配置模板,请参阅:
references/electron-security.md- 安全最佳实践references/ipc-patterns.md- IPC 通信模式references/build-config.md- 构建和打包配置scripts/scaffold.sh- 自动化脚手架脚本
脚手架生成后的检查清单
完成脚手架生成后,引导用户:
- ✅ 检查并自定义
package.json元数据 - ✅ 添加应用图标(适用于所有平台)
- ✅ 配置代码签名证书
- ✅ 设置用于自动更新的 GitHub 仓库
- ✅ 测试热重载和开发工作流
- ✅ 为所有目标平台构建
- ✅ 测试更新机制
- ✅ 检查安全设置
- ✅ 添加错误跟踪(Sentry 等)
- ✅ 创建用户文档
版本兼容性
此技能面向:
- Electron:v28+(最新稳定版)
- Node.js:v18+ LTS
- TypeScript:v5+
- Electron Forge:v7+
- Electron Builder:v24+
请始终查阅最新的 Electron 文档,了解可能导致不兼容的变更。