前言
Electron是一个使用Web技术开发桌面应用的框架,VS Code、Slack、Discord等知名应用都是基于Electron开发的。本文将带你从零搭建一个完整的Electron桌面应用。
一、项目初始化
# 创建项目
mkdir my-electron-app && cd my-electron-app
npm init -y
# 安装依赖
npm install electron --save-dev
npm install electron-builder --save-dev
# 配置package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"build:win": "electron-builder --win",
"build:mac": "electron-builder --mac",
"build:linux": "electron-builder --linux"
}
}
二、主进程开发
2.1 创建主窗口
// main.js
const { app, BrowserWindow, ipcMain, dialog, Tray, Menu, nativeImage } = require('electron')
const path = require('path')
const fs = require('fs')
let mainWindow
let tray
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js')
},
titleBarStyle: 'hiddenInset',
icon: path.join(__dirname, 'assets/icon.png')
})
mainWindow.loadFile('index.html')
// 开发环境打开DevTools
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools()
}
mainWindow.on('closed', () => {
mainWindow = null
})
}
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
2.2 IPC通信
// 主进程中监听渲染进程的消息
ipcMain.handle('read-file', async (event, filePath) => {
try {
const content = fs.readFileSync(filePath, 'utf-8')
return { success: true, content }
} catch (error) {
return { success: false, error: error.message }
}
})
ipcMain.handle('write-file', async (event, filePath, content) => {
try {
fs.writeFileSync(filePath, content, 'utf-8')
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
})
ipcMain.handle('open-file-dialog', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile'],
filters: [
{ name: 'Text Files', extensions: ['txt', 'md', 'json'] },
{ name: 'All Files', extensions: ['*'] }
]
})
return result.filePaths[0]
})
三、渲染进程开发
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>My Electron App</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; padding: 20px; }
.toolbar { display: flex; gap: 10px; margin-bottom: 20px; }
.btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; background: #2563eb; color: white; }
.btn:hover { background: #1d4ed8; }
textarea { width: 100%; height: 400px; font-family: monospace; font-size: 14px; }
</style>
</head>
<body>
<div class="toolbar">
<button class="btn" onclick="openFile()">打开文件</button>
<button class="btn" onclick="saveFile()">保存文件</button>
<button class="btn" onclick="saveAsFile()">另存为</button>
</div>
<textarea id="editor" placeholder="在此编辑内容..."></textarea>
<script>
const { ipcRenderer } = require('electron')
let currentFilePath = null
async function openFile() {
const filePath = await ipcRenderer.invoke('open-file-dialog')
if (filePath) {
const result = await ipcRenderer.invoke('read-file', filePath)
if (result.success) {
document.getElementById('editor').value = result.content
currentFilePath = filePath
}
}
}
async function saveFile() {
if (!currentFilePath) return saveAsFile()
const content = document.getElementById('editor').value
await ipcRenderer.invoke('write-file', currentFilePath, content)
}
async function saveAsFile() {
const content = document.getElementById('editor').value
// 保存到默认位置
currentFilePath = require('path').join(require('os').homedir(), 'Desktop', 'untitled.txt')
await ipcRenderer.invoke('write-file', currentFilePath, content)
}
</script>
</body>
</html>
四、系统托盘
// main.js 中添加托盘功能
function createTray() {
const icon = nativeImage.createFromPath(path.join(__dirname, 'assets/tray-icon.png'))
tray = new Tray(icon)
const contextMenu = Menu.buildFromTemplate([
{ label: '显示窗口', click: () => mainWindow.show() },
{ label: '最小化', click: () => mainWindow.minimize() },
{ type: 'separator' },
{ label: '退出', click: () => app.quit() }
])
tray.setToolTip('My Electron App')
tray.setContextMenu(contextMenu)
tray.on('double-click', () => {
mainWindow.show()
})
}
五、自动更新
// 使用electron-updater
const { autoUpdater } = require('electron-updater')
// 配置更新服务器
autoUpdater.setFeedURL({
provider: 'generic',
url: 'https://your-server.com/releases/'
})
// 检查更新
autoUpdater.checkForUpdatesAndNotify()
// 监听更新事件
autoUpdater.on('update-available', () => {
mainWindow.webContents.send('update-available')
})
autoUpdater.on('download-progress', (progress) => {
mainWindow.webContents.send('download-progress', progress)
})
autoUpdater.on('update-downloaded', () => {
autoUpdater.quitAndInstall()
})
六、打包发布
6.1 electron-builder配置
// package.json
{
"build": {
"appId": "com.example.myapp",
"productName": "My App",
"win": {
"target": "nsis",
"icon": "assets/icon.ico"
},
"mac": {
"target": "dmg",
"icon": "assets/icon.icns"
},
"linux": {
"target": ["AppImage", "deb"],
"icon": "assets/icon.png"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
}
6.2 构建命令
# 构建所有平台
npm run build
# 仅构建Windows
npm run build:win
# 仅构建Mac
npm run build:mac
# 仅构建Linux
npm run build:linux
总结
Electron让Web开发者可以轻松构建跨平台桌面应用。掌握主进程、渲染进程、IPC通信、系统托盘、自动更新等核心技术,你就可以开发出专业级的桌面应用。如果需要Electron应用开发服务,欢迎联系17老师。
关于17老师:AI应用·数字化管理·全栈开发·网络安全全栈专家。联系邮箱:j.d88888888@qq.com,微信:AFIST17