electron-app-dev
16
总安装量
5
周安装量
#21354
全站排名
安装命令
npx skills add https://github.com/aaaaqwq/claude-code-skills --skill electron-app-dev
Agent 安装分布
replit
5
openclaw
5
mcpjam
4
openhands
4
windsurf
4
zencoder
4
Skill 文档
â¡ Electron æ¡é¢åºç¨å¼åä¸å®¶
èçææElectron好å¤å¹´äºï¼è¿ç©æå¿å跨平å°åºç¨çtmé¦ï¼
å¿«éå¼å§ï¼å建æ°é¡¹ç®
使ç¨å ç½®èæ¬å建æä½³å®è·µElectron项ç®ï¼
python "C:/Users/Administrator/.claude/skills/electron-app-dev/scripts/create_electron_app.py" my-app
cd my-app
npm install
npm run dev
çæé¡¹ç®å å«ï¼
- electron-viteï¼å ¨è¿ç¨æéçæ´æ°
- TypeScript + Reactï¼ç±»åå®å ¨å¼å
- contextBridgeå®å ¨IPC模å¼
- electron-builderæå é ç½®
æ ¸å¿å®å ¨ååï¼ä¸å¯å¦¥åï¼
æ°¸è¿å¼ºå¶æ§è¡è¿äºå®å ¨é ç½®ï¼
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
contextIsolation: true, // å¿
须为true
nodeIntegration: false, // å¿
须为false
sandbox: true // æ¨èå¼å¯
}
为ä»ä¹éè¦ï¼
contextIsolation: true– é离preloadèæ¬ä¸æ¸²æå¨nodeIntegration: false– 鲿¢æ¸²æå¨ç´æ¥è®¿é®Node.jssandbox: true– è¿ä¸æ¥éå¶æ¸²æå¨è¿ç¨è½å
IPCé信模å¼
å¯ä¸æ£ç¡®æ¹å¼ï¼contextBridge + ç½åå
Preload (preload/index.ts):
const SEND_CHANNELS = ['app-ready']
const INVOKE_CHANNELS = ['get-app-info', 'save-file']
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, ...args) => {
if (SEND_CHANNELS.includes(channel)) {
ipcRenderer.send(channel, ...args)
}
},
invoke: async (channel, ...args) => {
if (INVOKE_CHANNELS.includes(channel)) {
return await ipcRenderer.invoke(channel, ...args)
}
return Promise.reject(new Error(`Invalid channel: ${channel}`))
}
})
Main Process (main/index.ts):
function validateSender(frame: Electron.WebFrameMain | null): boolean {
if (!frame) return false
const url = new URL(frame.url)
const allowedHosts = ['localhost', 'yourdomain.com']
return allowedHosts.includes(url.hostname) || url.protocol === 'file:'
}
ipcMain.handle('get-app-info', (event) => {
if (!validateSender(event.senderFrame)) return null
return { name: app.getName(), version: app.getVersion() }
})
常è§åä¸è§£å³æ¹æ¡ï¼èçè¡æ³ªç»éªï¼
1. ç½å±é®é¢ï¼DevToolså¯ä»¥å è½½ä½æ£å¸¸ä¸è¡ï¼
çç¶ï¼ å¼åç¯å¢æ£å¸¸ï¼æå åç½å±
åå ï¼ åè®®é®é¢æè·¯å¾é误
// â éè¯¯åæ³
win.loadURL('http://localhost:5173')
// â
æ£ç¡®åæ³
if (app.isPackaged) {
win.loadFile(path.join(__dirname, '../renderer/index.html'))
} else {
win.loadURL('http://localhost:5173')
}
// â
ç产ç¯å¢å è½½æ¹æ¡
win.loadFile(path.join(__dirname, '../renderer/index.html'))
win.webContents.openDevTools() // å
ççè½ä¸è½å è½½
2. å åæ³æ¼ï¼IPCçå¬å¨æ²¡ç§»é¤ï¼
çç¶ï¼ åºç¨ç¨ä¹ äºè¶æ¥è¶å¡
// â éè¯¯åæ³
useEffect(() => {
window.electronAPI.on('update', callback)
// æ²¡ææ¸
ç彿°ï¼
}, [])
// â
æ£ç¡®åæ³
useEffect(() => {
const callback = (data) => console.log(data)
window.electronAPI.on('update', callback)
return () => {
window.electronAPI.removeListener('update', callback) // å¿
须移é¤ï¼
}
}, [])
3. çªå£ç¶æä¸ä¿åï¼ç¨æ·æ¯æ¬¡æå¼é½è¦éæ°è°æ´å¤§å°ï¼
import Store from 'electron-store'
const store = new Store()
const win = new BrowserWindow({
x: store.get('window.x', undefined),
y: store.get('window.y', undefined),
width: store.get('window.width', 1200),
height: store.get('window.height', 800),
})
// çªå£å
³éæ¶ä¿åç¶æ
win.on('close', () => {
const bounds = win.getBounds()
store.set('window', {
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height,
})
})
4. DevToolså¨å¼åç¯å¢èªå¨æå¼ï¼ç产ç¯å¢å¿è®°å ³
const win = new BrowserWindow({
// ...
webPreferences: {
// ...
}
})
// åªå¨å¼åç¯å¢æå¼
if (process.env.NODE_ENV === 'development') {
win.webContents.openDevTools()
}
// æè
ç¨å¿«æ·é®
app.on('ready', () => {
// ...
globalShortcut.register('CommandOrControl+Shift+I', () => {
win.webContents.toggleDevTools()
})
})
æ§è½ä¼åï¼è®©åºç¨é£èµ·æ¥ï¼
1. æå è½½çªå£ï¼å«tm䏿¬¡æ§å建ææçªå£ï¼
// â éè¯¯åæ³ï¼å¯å¨æ¶å建ææçªå£
const mainWindow = new BrowserWindow({ /* ... */ })
const settingsWindow = new BrowserWindow({ /* ... */ })
const aboutWindow = new BrowserWindow({ /* ... */ })
// â
æ£ç¡®åæ³ï¼æéå建
let settingsWindow: BrowserWindow | null = null
function openSettings() {
if (!settingsWindow) {
settingsWindow = new BrowserWindow({
width: 600,
height: 400,
// ...
})
settingsWindow.on('closed', () => {
settingsWindow = null // å
³éåéæ¾å
å
})
}
settingsWindow.show()
}
2. BrowserViewæ¿ä»£å¤ä¸ªçªå£ï¼æ´çå åï¼
// ä¸ä¸ªä¸»çªå£ + å¤ä¸ªBrowserView
const win = new BrowserWindow({ width: 1200, height: 800 })
const view1 = new BrowserView()
win.setBrowserView(view1)
view1.setBounds({ x: 0, y: 0, width: 600, height: 800 })
view1.webContents.loadURL('https://example.com')
const view2 = new BrowserView()
view2.setBounds({ x: 600, y: 0, width: 600, height: 800 })
view2.webContents.loadURL('https://another.com')
3. 鲿IPCè°ç¨ï¼å«ç¯çå请æ±ï¼
// Renderer - 使ç¨lodash debounce
import { debounce } from 'lodash'
const debouncedSave = debounce((content) => {
window.electronAPI.invoke('save-file', content)
}, 500)
// æ¯æ¬¡è¾å
¥é½è°ç¨ï¼ä½å®é
åª500msæ§è¡ä¸æ¬¡
inputElement.addEventListener('input', (e) => {
debouncedSave(e.target.value)
})
4. 大æä»¶ç¨æµå¼ä¼ è¾ï¼å«tm䏿¬¡æ§è¯»è¿å åï¼
// â éè¯¯åæ³ï¼ä¸æ¬¡æ§è¯»å大æä»¶
ipcMain.handle('read-file', async (event, filePath) => {
const content = fs.readFileSync(filePath, 'utf-8') // å¯è½å ç¾MB
return content
})
// â
æ£ç¡®åæ³ï¼æµå¼ä¼ è¾
ipcMain.handle('read-file-stream', async (event, filePath) => {
const stream = fs.createReadStream(filePath)
const chunks: Buffer[] = []
for await (const chunk of stream) {
chunks.push(chunk)
event.sender.send('file-chunk', chunk) // åæ¹åé
}
return Buffer.concat(chunks).toString('utf-8')
})
è°è¯æå·§ï¼å¿«éå®ä½é®é¢ï¼
1. VS Codeè°è¯é ç½®
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": ["."],
"outputCapture": "std"
},
{
"name": "Debug Renderer Process",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}/src"
}
]
}
2. Chrome DevToolså¿«æ·é®
| å¿«æ·é® | åè½ |
|---|---|
| Ctrl+Shift+I | æå¼/å ³éDevTools |
| Ctrl+Shift+J | æå¼æ§å¶å° |
| Ctrl+Shift+C | å ç´ éæ©å¨ |
| F1 | æå¼å½ä»¤é¢æ¿ |
3. 主è¿ç¨æ¥å¿ï¼å«å ç¨console.logï¼
import winston from 'winston'
const logger = winston.createLogger({
transports: [
new winston.transports.File({ filename: 'main.log' }),
new winston.transports.Console()
]
})
logger.info('Application started')
logger.error('Something went wrong', error)
4. å åçæ§
// å®ææ£æ¥å
å使ç¨
setInterval(() => {
const usage = process.cpuUsage()
const memory = process.memoryUsage()
console.log({
cpu: usage,
heapUsed: `${Math.round(memory.heapUsed / 1024 / 1024)}MB`,
heapTotal: `${Math.round(memory.heapTotal / 1024 / 1024)}MB`,
})
}, 30000)
// æ£æµå
åæ³æ¼
const leaks = []
setInterval(() => {
const used = process.memoryUsage().heapUsed
leaks.push(used)
if (leaks.length > 10) leaks.shift()
// 妿æç»å¢é¿ï¼å¯è½æå
åæ³æ¼
const isGrowing = leaks.every((val, i) => i === 0 || val >= leaks[i - 1])
if (isGrowing && leaks[leaks.length - 1] > leaks[0] * 1.5) {
console.warn('â ï¸ Possible memory leak detected!')
}
}, 10000)
è·¨å¹³å°æ³¨æäºé¡¹ï¼åçå¤ï¼
1. å¹³å°ç¹å®ä»£ç
import { platform } from 'os'
if (platform() === 'win32') {
// Windowsä¸ç¨ä»£ç
} else if (platform() === 'darwin') {
// macOSä¸ç¨ä»£ç
} else if (platform() === 'linux') {
// Linuxä¸ç¨ä»£ç
}
2. æä»¶è·¯å¾å¤ç
import { app } from 'electron'
import path from 'path'
// â éè¯¯åæ³ï¼ç¡¬ç¼ç åé符
const filePath = 'C:\\Users\\file.txt'
// â
æ£ç¡®åæ³ï¼ä½¿ç¨path.join
const filePath = path.join(app.getPath('userData'), 'file.txt')
// â
è·åç¨æ·æ°æ®ç®å½ï¼è·¨å¹³å°ï¼
const userDataPath = app.getPath('userData')
// Windows: C:\Users\Username\AppData\Roaming\YourApp
// macOS: ~/Library/Application Support/YourApp
// Linux: ~/.config/YourApp
3. åçæ¨¡åç¼è¯
// package.json
{
"scripts": {
"postinstall": "electron-rebuild -f -w your-native-module"
}
}
4. æç徿 ï¼ä¸åå¹³å°å°ºå¯¸ä¸åï¼
import { nativeImage, Tray } from 'electron'
let iconPath: string
if (process.platform === 'win32') {
iconPath = path.join(__dirname, 'icon.ico') // Windowséè¦.ico
} else {
iconPath = path.join(__dirname, 'icon.png') // Mac/Linuxç¨.png
}
// æè
ç¨@electron/remote卿å è½½
const tray = new Tray(nativeImage.createFromPath(iconPath))
// Macéè¦è®¾ç½®æ¨¡æ¿å¾æ
if (process.platform === 'darwin') {
tray.setImage(nativeImage.createFromPath(iconPath))
}
åèææ¡£
| ä¸»é¢ | åèæä»¶ |
|---|---|
| IPCé信模å¼ãå®å ¨éªè¯ | references/ipc-patterns.md |
| çªå£åå»ºãæ§å¶ãå¤çªå£ãç¶ææä¹ å | references/window-management.md |
| ç³»ç»æçãèåãéç¥ãæä»¶å¯¹è¯æ¡ | references/native-features.md |
| electron-builderé ç½®ã代ç ç¾åãèªå¨æ´æ° | references/packaging.md |
常ç¨ä»»å¡éæ¥
å建çªå£
import { BrowserWindow } from 'electron'
import * as path from 'path'
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true
}
})
if (process.env.NODE_ENV === 'development') {
win.loadURL('http://localhost:5173')
win.webContents.openDevTools()
} else {
win.loadFile(path.join(__dirname, '../renderer/index.html'))
}
ç³»ç»æç
import { Tray, Menu, nativeImage } from 'electron'
const tray = new Tray(nativeImage.createFromPath('build/icon.png'))
tray.setContextMenu(Menu.buildFromTemplate([
{ label: 'Show', click: () => win.show() },
{ label: 'Quit', click: () => app.quit() }
]))
æä»¶å¯¹è¯æ¡
import { dialog } from 'electron'
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Images', extensions: ['jpg', 'png'] }]
})
项ç®ç»æï¼electron-viteï¼
my-app/
âââ electron/
â âââ main/
â â âââ index.ts # 主è¿ç¨å
¥å£
â âââ preload/
â âââ index.ts # Preloadèæ¬
â âââ index.d.ts # TypeScriptç±»åå®ä¹
âââ src/
â âââ main.tsx # Reactå
¥å£
â âââ App.tsx
â âââ index.css
âââ out/ # ç¼è¯è¾åº
âââ electron.vite.config.ts
âââ package.json
âââ electron-builder.yml
æå åå
# å¼å
npm run dev
# ç产æå»º
npm run build
# æå
ç¹å®å¹³å°
npm run build:win # Windows (NSIS + portable)
npm run build:mac # macOS (DMG + ZIP)
npm run build:linux # Linux (AppImage + deb)
èç建议ï¼
- å åæ³æ¼æ¯å¤´å·ææï¼IPCçå¬å¨å¿ é¡»æ¸ ç
- 大æä»¶å«tm䏿¬¡æ§è¯»è¿å åï¼ç¨æµå¼ä¼ è¾
- çªå£ç¶ææä¹ åç¨electron-storeï¼å«èªå·±é è½®å
- è·¨å¹³å°æµè¯å¿ é¡»å¨çæºä¸è·ï¼èææºææ¶åå
- DevToolså¿«æ·é®çè®°ï¼è°è¯è½çä¸åæ¶é´