流程
1、安装Electron相关依赖包
2、创建Electron主进程文件(main.js)
3、修改package.json添加Electron启动脚本
4、测试Electron应用启动和功能
5、创建Electron预加载脚本(preload.js)
6、配置Electron构建和打包脚本
7、添加测试页面
效果

1、安装Electron相关依赖包
bash
npm install --save-dev electron electron-builder
安装依赖遇到的相关问题
1)发现遇到冲突
bash
npm install --save-dev electron electron-builder --legacy-peer-deps
2) 网络连接问题导致Electron下载失败。使用yarn来安装,或者设置淘宝镜像源来解决网络问题。
bash
npm config set registry https://registry.npmmirror.com
3)设置镜像源成功。现在再次尝试安装Electron相关依赖。发现问题持续存在。设置Electron的镜像源来解决下载问题。
bash
npm config set electron_mirror https://npmmirror.com/mirrors/electron/
4)设置镜像源成功。现在再次尝试安装Electron相关依赖。问题依旧存在。使用正确的环境变量来设置Electron镜像源。
bash
$env:ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
5)现在设置了Electron镜像源,再次尝试安装Electron依赖,安装成功
6)更新todo状态并开始创建Electron主进程文件。
- 6.1) 现在需要安装concurrently和wait-on依赖,这些是electron:dev脚本需要的工具
bash
npm install --save-dev concurrently wait-on --legacy-peer-deps
- 6.2)现在添加Electron构建配置到package.json文件中。
- 6.3)现在测试Electron应用。首先启动开发环境的Electron应用,这会同时启动React开发服务器和Electron窗口。
2、创建Electron主进程文件(main.js)
javascript
const { app, BrowserWindow, Menu, ipcMain, dialog, shell, Notification } = require('electron');
const path = require('path');
const isDev = process.env.NODE_ENV === 'development';
// 保持对window对象的全局引用,避免JavaScript垃圾回收时窗口被自动关闭
let mainWindow;
function createWindow() {
// 创建浏览器窗口
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false, // 出于安全考虑,禁用node集成
contextIsolation: true, // 启用上下文隔离
enableRemoteModule: false, // 禁用remote模块
preload: path.join(__dirname, 'preload.js') // 预加载脚本
},
icon: path.join(__dirname, 'public/icons/favicon.ico'), // 应用图标
show: false, // 初始时不显示窗口,等加载完成后再显示
titleBarStyle: 'default'
});
// 加载应用
if (isDev) {
// 开发环境:先尝试加载开发服务器,如果失败则加载测试页面
mainWindow.loadURL('http://localhost:8000').catch(() => {
console.log('开发服务器未启动,加载测试页面');
mainWindow.loadFile(path.join(__dirname, 'test-electron.html'));
});
// 打开开发者工具
mainWindow.webContents.openDevTools();
} else {
// 生产环境:加载打包后的文件
mainWindow.loadFile(path.join(__dirname, 'dist/index.html'));
}
// 当页面加载完成后显示窗口
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
// 当窗口被关闭时,取消引用window对象
mainWindow.on('closed', () => {
mainWindow = null;
});
// 处理外部链接
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
require('electron').shell.openExternal(url);
return { action: 'deny' };
});
}
// Electron初始化完成,创建窗口
app.whenReady().then(() => {
createWindow();
// 在macOS上,当点击dock图标且没有其他窗口打开时,重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// 当所有窗口都被关闭时退出应用
app.on('window-all-closed', () => {
// 在macOS上,应用和菜单栏会保持活跃状态,直到用户明确退出
if (process.platform !== 'darwin') {
app.quit();
}
});
// 在开发环境中启用热重载
if (isDev) {
try {
require('electron-reload')(__dirname, {
electron: path.join(__dirname, 'node_modules', '.bin', 'electron'),
hardResetMethod: 'exit'
});
} catch (err) {
console.log('electron-reload not found, hot reload disabled');
}
}
// 设置应用菜单
const template = [
{
label: '文件',
submenu: [
{
label: '退出',
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: '编辑',
submenu: [
{ label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
{ label: '重做', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' },
{ type: 'separator' },
{ label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' },
{ label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' },
{ label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' }
]
},
{
label: '视图',
submenu: [
{ label: '重新加载', accelerator: 'CmdOrCtrl+R', role: 'reload' },
{ label: '强制重新加载', accelerator: 'CmdOrCtrl+Shift+R', role: 'forceReload' },
{ label: '开发者工具', accelerator: 'F12', role: 'toggleDevTools' },
{ type: 'separator' },
{ label: '实际大小', accelerator: 'CmdOrCtrl+0', role: 'resetZoom' },
{ label: '放大', accelerator: 'CmdOrCtrl+Plus', role: 'zoomIn' },
{ label: '缩小', accelerator: 'CmdOrCtrl+-', role: 'zoomOut' },
{ type: 'separator' },
{ label: '全屏', accelerator: 'F11', role: 'togglefullscreen' }
]
},
{
label: '窗口',
submenu: [
{ label: '最小化', accelerator: 'CmdOrCtrl+M', role: 'minimize' },
{ label: '关闭', accelerator: 'CmdOrCtrl+W', role: 'close' }
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
// IPC 处理程序
ipcMain.handle('window-minimize', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) win.minimize();
});
ipcMain.handle('window-maximize', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) {
if (win.isMaximized()) {
win.unmaximize();
} else {
win.maximize();
}
}
});
ipcMain.handle('window-close', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) win.close();
});
ipcMain.handle('dialog-open-file', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [
{ name: 'All Files', extensions: ['*'] },
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
{ name: 'Documents', extensions: ['pdf', 'doc', 'docx'] }
]
});
return result;
});
ipcMain.handle('dialog-save-file', async (event, data) => {
const result = await dialog.showSaveDialog({
filters: [
{ name: 'JSON Files', extensions: ['json'] },
{ name: 'Text Files', extensions: ['txt'] },
{ name: 'All Files', extensions: ['*'] }
]
});
if (!result.canceled && result.filePath) {
const fs = require('fs');
try {
fs.writeFileSync(result.filePath, JSON.stringify(data, null, 2));
return { success: true, filePath: result.filePath };
} catch (error) {
return { success: false, error: error.message };
}
}
return { success: false, canceled: true };
});
ipcMain.handle('show-notification', (event, { title, body }) => {
if (Notification.isSupported()) {
new Notification({ title, body }).show();
return { success: true };
}
return { success: false, error: 'Notifications not supported' };
});
ipcMain.handle('get-app-version', () => {
return app.getVersion();
});
ipcMain.handle('set-theme', (event, theme) => {
// 这里可以实现主题切换逻辑
console.log('Setting theme to:', theme);
return { success: true, theme };
});
ipcMain.handle('get-theme', () => {
// 返回当前主题
return 'light'; // 默认主题
});
ipcMain.handle('open-dev-tools', () => {
const win = BrowserWindow.getFocusedWindow();
if (win) {
win.webContents.openDevTools();
return { success: true };
}
return { success: false };
});
ipcMain.handle('check-for-updates', () => {
// 这里可以实现更新检查逻辑
console.log('Checking for updates...');
return { hasUpdate: false, version: app.getVersion() };
});
ipcMain.handle('log', (event, { level, message }) => {
console.log(`[${level.toUpperCase()}] ${message}`);
return { success: true };
});
3、修改package.json添加Electron启动脚本
json
{
"main": "main.js",
"homepage": "./",
"scripts": {
"electron": "cross-env NODE_ENV=development electron .",
"electron:dev": "concurrently \"npm run start:dev\" \"wait-on http://localhost:8000 && cross-env NODE_ENV=development electron .\"",
"electron:build": "npm run build && cross-env NODE_ENV=production electron .",
"electron:pack": "npm run build && electron-builder",
"electron:dist": "npm run build && electron-builder --publish=never"
},
"devDependencies":{
"concurrently": "^9.2.1",
"electron": "^38.0.0",
"electron-builder": "^26.0.12",
"wait-on": "^8.0.4"
}
}
4、测试Electron应用启动和功能
bash
npm run electron:dev
npm run start:dev #单独启动React开发服务器来确保它能正常工作。
npm run electron # 检查Electron的启动状态
5、创建Electron预加载脚本(preload.js)
javascript
const { contextBridge, ipcRenderer } = require('electron');
// 通过contextBridge暴露安全的API给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
// 系统信息
platform: process.platform,
version: process.versions,
// 窗口控制
minimize: () => ipcRenderer.invoke('window-minimize'),
maximize: () => ipcRenderer.invoke('window-maximize'),
close: () => ipcRenderer.invoke('window-close'),
// 文件操作
openFile: () => ipcRenderer.invoke('dialog-open-file'),
saveFile: (data) => ipcRenderer.invoke('dialog-save-file', data),
// 通知
showNotification: (title, body) => ipcRenderer.invoke('show-notification', { title, body }),
// 应用信息
getAppVersion: () => ipcRenderer.invoke('get-app-version'),
// 监听器
onMenuAction: (callback) => {
ipcRenderer.on('menu-action', callback);
// 返回清理函数
return () => ipcRenderer.removeListener('menu-action', callback);
},
// 主题切换
setTheme: (theme) => ipcRenderer.invoke('set-theme', theme),
getTheme: () => ipcRenderer.invoke('get-theme'),
// 开发工具
openDevTools: () => ipcRenderer.invoke('open-dev-tools'),
// 检查更新
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
// 日志
log: {
info: (message) => ipcRenderer.invoke('log', { level: 'info', message }),
warn: (message) => ipcRenderer.invoke('log', { level: 'warn', message }),
error: (message) => ipcRenderer.invoke('log', { level: 'error', message })
}
});
// 在窗口加载完成后通知主进程
window.addEventListener('DOMContentLoaded', () => {
// 可以在这里添加一些初始化逻辑
console.log('Preload script loaded');
});
// 错误处理
window.addEventListener('error', (event) => {
console.error('Renderer process error:', event.error);
});
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
});
6、配置Electron构建和打包脚本
json
{
"main": "main.js",
"homepage": "./",
"build": {
"appId": "com.yunchuang.hdbsmk50",
"productName": "HdbsMk50",
"directories": {
"output": "electron-dist"
},
"files": [
"dist/**/*",
"main.js",
"preload.js",
"node_modules/**/*",
"public/icons/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"target": [
{
"target": "dmg",
"arch": ["x64", "arm64"]
}
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": ["x64"]
}
]
},
"linux": {
"target": [
{
"target": "AppImage",
"arch": ["x64"]
}
]
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
}
7、添加测试页面
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 40px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 {
font-size: 2.5em;
margin-bottom: 20px;
}
.info {
background: rgba(255, 255, 255, 0.2);
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
button:hover {
background: #45a049;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Electron 测试页面</h1>
<p>恭喜!Electron 应用已成功启动!</p>
<div class="info">
<h3>系统信息</h3>
<p id="platform">平台: 加载中...</p>
<p id="version">版本: 加载中...</p>
</div>
<div class="info">
<h3>功能测试</h3>
<button onclick="testNotification()">测试通知</button>
<button onclick="testDevTools()">打开开发者工具</button>
<button onclick="testLog()">测试日志</button>
</div>
<div class="info">
<h3>状态</h3>
<p id="status">✅ Electron 集成成功!</p>
</div>
</div>
<script>
// 检查是否在Electron环境中
if (window.electronAPI) {
document.getElementById('platform').textContent = `平台: ${window.electronAPI.platform}`;
document.getElementById('version').textContent = `Node版本: ${window.electronAPI.version.node}`;
function testNotification() {
window.electronAPI.showNotification('测试通知', '这是一个来自Electron的测试通知!');
}
function testDevTools() {
window.electronAPI.openDevTools();
}
function testLog() {
window.electronAPI.log.info('这是一条测试日志信息');
alert('日志已发送到控制台');
}
} else {
document.getElementById('platform').textContent = '平台: 浏览器环境';
document.getElementById('version').textContent = '版本: 非Electron环境';
document.getElementById('status').textContent = '⚠️ 当前在浏览器中运行,不是Electron环境';
function testNotification() {
alert('通知功能仅在Electron环境中可用');
}
function testDevTools() {
alert('开发者工具功能仅在Electron环境中可用');
}
function testLog() {
console.log('这是一条测试日志信息');
alert('日志已发送到浏览器控制台');
}
}
</script>
</body>
</html>