以下是一个基础但功能完整的Electron应用结构:
📁 项目结构
my-electron-app/
├── package.json
├── main.js
├── preload.js
├── renderer.js
├── index.html
├── style.css
└── README.md
1. 📦 package.json
json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "A minimal Electron application",
"main": "main.js",
"scripts": {
"start": "electron .",
"dev": "electron .",
"build": "electron-builder",
"pack": "electron-builder --dir"
},
"repository": {
"type": "git",
"url": "https://github.com/your-username/my-electron-app"
},
"keywords": [
"Electron",
"quick",
"start",
"tutorial"
],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.0.0"
},
"dependencies": {
"axios": "^1.6.0"
},
"build": {
"appId": "com.example.my-electron-app",
"productName": "My Electron App",
"directories": {
"output": "dist"
},
"files": [
"dist/**/*",
"node_modules/**/*",
"*.js",
"*.html",
"*.css"
],
"win": {
"target": "nsis"
},
"mac": {
"category": "public.app-category.utilities"
},
"linux": {
"target": "AppImage"
}
}
}
2. 🖥️ main.js (主进程)
javascript
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
// 全局变量
let mainWindow;
// 创建窗口函数
function createWindow() {
// 创建浏览器窗口
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
},
icon: path.join(__dirname, 'assets/icon.png')
});
// 加载应用的index.html
mainWindow.loadFile('index.html');
// 开发环境下打开开发者工具
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
// 窗口关闭事件
mainWindow.on('closed', function () {
mainWindow = null;
});
}
// 应用准备就绪时创建窗口
app.whenReady().then(() => {
createWindow();
// macOS应用在Dock中点击时重新创建窗口
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 所有窗口关闭时退出应用(Windows & Linux)
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit();
});
// IPC通信处理
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile']
});
if (canceled) {
return null;
} else {
return filePaths[0];
}
});
ipcMain.handle('dialog:saveFile', async (event, data) => {
const { canceled, filePath } = await dialog.showSaveDialog(mainWindow, {
defaultPath: 'document.txt'
});
if (canceled) {
return false;
} else {
try {
fs.writeFileSync(filePath, data);
return true;
} catch (error) {
return false;
}
}
});
ipcMain.handle('read-file', async (event, filePath) => {
try {
const data = fs.readFileSync(filePath, 'utf8');
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
});
// 菜单模板
const menuTemplate = [
{
label: '文件',
submenu: [
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile']
});
if (!result.canceled) {
mainWindow.webContents.send('file-opened', result.filePaths[0]);
}
}
},
{
label: '保存',
accelerator: 'CmdOrCtrl+S',
click: () => {
mainWindow.webContents.send('save-file');
}
},
{ type: 'separator' },
{
label: '退出',
accelerator: 'CmdOrCtrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: '编辑',
submenu: [
{ role: 'undo', label: '撤销' },
{ role: 'redo', label: '重做' },
{ type: 'separator' },
{ role: 'cut', label: '剪切' },
{ role: 'copy', label: '复制' },
{ role: 'paste', label: '粘贴' }
]
},
{
label: '视图',
submenu: [
{ role: 'reload', label: '重新加载' },
{ role: 'forcereload', label: '强制重新加载' },
{ role: 'toggledevtools', label: '切换开发者工具' },
{ type: 'separator' },
{ role: 'resetzoom', label: '重置缩放' },
{ role: 'zoomin', label: '放大' },
{ role: 'zoomout', label: '缩小' },
{ type: 'separator' },
{ role: 'togglefullscreen', label: '切换全屏' }
]
},
{
label: '帮助',
submenu: [
{
label: '学习更多',
click: async () => {
const { shell } = require('electron');
await shell.openExternal('https://electronjs.org');
}
}
]
}
];
// 构建菜单
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
// 导出用于测试
module.exports = { createWindow, mainWindow };
3. 🔧 preload.js (预加载脚本)
javascript
const { contextBridge, ipcRenderer } = require('electron');
// 安全地暴露API到渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
// 对话框相关
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (data) => ipcRenderer.invoke('dialog:saveFile', data),
// 文件操作
readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
// 事件监听
onFileOpened: (callback) => ipcRenderer.on('file-opened', (_event, filePath) => callback(filePath)),
onSaveFile: (callback) => ipcRenderer.on('save-file', () => callback()),
// 移除监听器
removeAllListeners: (channel) => ipcRenderer.removeAllListeners(channel)
});
// 环境变量
contextBridge.exposeInMainWorld('env', {
isDev: process.env.NODE_ENV === 'development'
});
4. 🎨 index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Electron App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<h1>Electron 应用示例</h1>
<div class="toolbar">
<button id="openBtn" class="btn">打开文件</button>
<button id="saveBtn" class="btn primary">保存文件</button>
<button id="clearBtn" class="btn">清空</button>
</div>
</div>
<div class="container">
<div class="sidebar">
<h3>功能列表</h3>
<ul>
<li class="active">文本编辑器</li>
<li>文件管理</li>
<li>系统信息</li>
<li>设置</li>
</ul>
</div>
<div class="main-content">
<div class="editor-container">
<textarea
id="editor"
placeholder="在这里输入您的内容..."
spellcheck="false"></textarea>
</div>
<div class="status-bar">
<span id="status">就绪</span>
<span id="charCount">字符数: 0</span>
</div>
</div>
</div>
<div class="notification" id="notification">
操作成功!
</div>
<script src="renderer.js"></script>
</body>
</html>
5. 🎨 style.css
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
color: #333;
height: 100vh;
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header h1 {
margin-bottom: 15px;
}
.toolbar {
display: flex;
gap: 10px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.btn:active {
transform: translateY(0);
}
.btn.primary {
background-color: #007bff;
color: white;
}
.btn.primary:hover {
background-color: #0056b3;
}
.container {
display: flex;
height: calc(100vh - 120px);
}
.sidebar {
width: 200px;
background-color: #2c3e50;
color: white;
padding: 20px 0;
overflow-y: auto;
}
.sidebar h3 {
padding: 0 20px 15px;
border-bottom: 1px solid #34495e;
margin-bottom: 15px;
}
.sidebar ul {
list-style: none;
}
.sidebar li {
padding: 12px 20px;
cursor: pointer;
transition: background-color 0.3s;
}
.sidebar li:hover {
background-color: #34495e;
}
.sidebar li.active {
background-color: #3498db;
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
}
.editor-container {
flex: 1;
padding: 20px;
}
#editor {
width: 100%;
height: 100%;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 14px;
resize: none;
outline: none;
background-color: white;
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
}
#editor:focus {
border-color: #3498db;
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1), 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background-color: #ecf0f1;
border-top: 1px solid #ddd;
font-size: 12px;
color: #7f8c8d;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background-color: #27ae60;
color: white;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transform: translateX(400px);
transition: transform 0.3s ease;
z-index: 1000;
}
.notification.show {
transform: translateX(0);
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.sidebar {
width: 100%;
height: auto;
}
.sidebar ul {
display: flex;
overflow-x: auto;
}
.sidebar li {
white-space: nowrap;
}
}
6. 🖱️ renderer.js (渲染进程)
javascript
// 渲染进程逻辑
document.addEventListener('DOMContentLoaded', function() {
const editor = document.getElementById('editor');
const openBtn = document.getElementById('openBtn');
const saveBtn = document.getElementById('saveBtn');
const clearBtn = document.getElementById('clearBtn');
const status = document.getElementById('status');
const charCount = document.getElementById('charCount');
const notification = document.getElementById('notification');
// 显示通知
function showNotification(message) {
notification.textContent = message;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// 更新状态栏
function updateStatusBar() {
const text = editor.value;
const count = text.length;
charCount.textContent = `字符数: ${count}`;
}
// 更新状态文本
function updateStatus(text) {
status.textContent = text;
}
// 打开文件
openBtn.addEventListener('click', async () => {
try {
updateStatus('正在打开文件...');
const filePath = await window.electronAPI.openFile();
if (filePath) {
const result = await window.electronAPI.readFile(filePath);
if (result.success) {
editor.value = result.data;
updateStatusBar();
updateStatus(`已打开: ${filePath}`);
showNotification('文件打开成功!');
} else {
updateStatus('打开文件失败');
showNotification('文件打开失败!');
}
} else {
updateStatus('取消打开文件');
}
} catch (error) {
console.error('打开文件错误:', error);
updateStatus('打开文件时发生错误');
showNotification('打开文件时发生错误!');
}
});
// 保存文件
saveBtn.addEventListener('click', async () => {
try {
updateStatus('正在保存文件...');
const success = await window.electronAPI.saveFile(editor.value);
if (success) {
updateStatus('文件保存成功');
showNotification('文件保存成功!');
} else {
updateStatus('文件保存失败');
showNotification('文件保存失败!');
}
} catch (error) {
console.error('保存文件错误:', error);
updateStatus('保存文件时发生错误');
showNotification('保存文件时发生错误!');
}
});
// 清空编辑器
clearBtn.addEventListener('click', () => {
editor.value = '';
updateStatusBar();
updateStatus('编辑器已清空');
showNotification('编辑器已清空!');
});
// 编辑器内容变化事件
editor.addEventListener('input', () => {
updateStatusBar();
updateStatus('文档已修改');
});
// 键盘快捷键
document.addEventListener('keydown', (e) => {
// Ctrl+S 保存
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
saveBtn.click();
}
// Ctrl+O 打开
if (e.ctrlKey && e.key === 'o') {
e.preventDefault();
openBtn.click();
}
});
// 监听主进程发送的事件
window.electronAPI.onFileOpened((filePath) => {
// 这里可以处理文件打开后的逻辑
console.log('文件已打开:', filePath);
});
window.electronAPI.onSaveFile(() => {
saveBtn.click();
});
// 初始化状态栏
updateStatusBar();
updateStatus('应用已启动');
// 开发环境提示
if (window.env.isDev) {
showNotification('开发模式');
}
});
// 页面卸载时清理监听器
window.addEventListener('beforeunload', () => {
window.electronAPI.removeAllListeners('file-opened');
window.electronAPI.removeAllListeners('save-file');
});
7. 📝 README.md
markdown
# My Electron App
一个基于 Electron 的桌面应用程序示例。
## 功能特性
- 📝 文本编辑器
- 📁 文件打开/保存
- 🎨 美观的用户界面
- ⌨️ 键盘快捷键支持
- 🖥️ 跨平台支持 (Windows, macOS, Linux)
## 安装
```bash
# 克隆仓库
git clone your-repo-url
cd my-electron-app
# 安装依赖
npm install
开发
bash
# 启动开发服务器
npm start
# 或者