前提是你得有一个前端项目且能成功运行起来哈!
安装electron相关包
# 这行命令很重要,否则会导致electron打包下载的zip一直失败
pnpm install -D concurrently cross-env wait-on
pnpm install -D electron electron-builder electron-reloader
pnpm install @electron/remote
如果在安装electron相关包的时候出现node install.js卡住的问题,可以跳过该脚本的执行,安装完包之后,再按顺序运行如下命令修复一下问题:
pushd node_modules/electron
# 在这一步,会下载适配你自己电脑的electron压缩包
node install.js
popd
最后执行popd后返回你当前项目的根目录在电脑中的目录地址的话,此时,相关包均安装完成。
配置package.json
{
"private": true,
"author": "EmmaLu",
"description": "这是一个示例 Electron 应用程序",
"name": "sdf-web-electron",
"version": "2.1.0",
"main": "app/main.js",
"scripts": {
"dev": "umi dev",
"build": "umi build",
"postinstall": "umi setup",
"setup": "umi setup",
"start": "npm run dev",
"electron": "pnpm build && NODE_ENV=production electron app/main.js",
"electron-dev": "concurrently \"cross-env BROWSER=none pnpm dev\" \"wait-on http://localhost:8000 && cross-env NODE_ENV=development electron app/main.js\"",
"pre-electron-build:win": "pnpm build && electron-builder install-app-deps",
"pre-electron-build:mac": "pnpm build && electron-builder install-app-deps",
"electron-build:win": "pnpm build && electron-builder build --win --x64",
"electron-build:mac": "pnpm build && electron-builder build --mac --arm64"
},
"build": {
"electronDownload": {
"cache": "./.electron-cache"
},
"directories": {
"output": "dist-app/${os}-${arch}"
},
"icon": "app/assets/favicon.ico",
"mac": {
"target": [
{
"target": "dmg",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}_setup_${version}.${ext}"
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}_setup_${version}.${ext}"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"installerIcon": "app/assets/favicon.ico",
"uninstallerIcon": "app/assets/favicon.ico"
}
},
"dependencies": {
...
},
"devDependencies": {
...
}
}
说明:
“electron-dev”:本机桌面端的开发模式;
“electron”:本地桌面端实时展示;
“pre-electron-build:win”:安装windows版应用程序依赖项;
“pre-electron-build:mac”:安装MAC版应用程序依赖;
“electron-build:win”:打包成windows桌面端应用可执行文件.exe;
“electron-build:mac”:打包成MAC桌面端应用可执行文件.dmg;
实现HTTP服务与Electron窗口创建
目录结构如下:

说明:本地启动 umi 项目的 HTTP 服务
在项目根目录下创建app文件夹,创建www.js文件,其具体内容如下:
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
// const debug = require('debug')('server');
let server = null;
// let port = process.argv[2] || 8080;
let port = process.env.ELECTRON_SERVER_PORT || process.argv[2] || 8000;
console.log('port-www.js', port)
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
let bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
let addr = server.address();
let bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
// debug('Listening on ' + bind);
}
const startServer = (customPort) => {
/**
* Listen on provided port, on all network interfaces.
*/
port = customPort || port;
if (server) {
server.close();
}
server = http
.createServer(function (request, response) {
console.log('request', request.url)
let uri = url.parse(request.url).pathname;
let filename = path.join(__dirname, '../dist', uri);
let contentTypesByExtension = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
};
fs.exists(filename, function (exists) {
if (!exists) {
response.writeHead(404, { 'Content-Type': 'text/plain' });
response.write('404 Not Found\n');
response.end();
return;
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, 'binary', function (err, file) {
if (err) {
response.writeHead(500, { 'Content-Type': 'text/plain' });
response.write(err + '\n');
response.end();
return;
}
let headers = {};
let contentType = contentTypesByExtension[path.extname(filename)];
if (contentType) headers['Content-Type'] = contentType;
response.writeHead(200, headers);
response.write(file, 'binary');
response.end();
});
});
})
.listen(parseInt(port, 10), '0.0.0.0');
// server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
};
const getServer = () => server;
// 暴露一个函数,用于启动服务
module.exports = { startServer, getServer };
在app目录下创建main.js文件,用于创建一个 electron 窗口,其具体内容如下:
// electron打包配置
const {
app,
BrowserWindow,
globalShortcut,
ipcMain
} = require('electron');
const path = require('path');
const { startServer, getServer } = require('./www');
const fs = require('fs');
const isPro = process.env.NODE_ENV !== 'development';
const remote = require('@electron/remote/main');
remote.initialize();
let mainWindow;
let port = process.argv[2] || 8000;
console.log('port-main.js', port)
function createWindow() {
startServer(port);
const userDataPath = app.getPath('userData');
const configPath = path.join(userDataPath, 'config.json');
let savedConfig = {};
if (fs.existsSync(configPath)) {
savedConfig = JSON.parse(fs.readFileSync(configPath));
port = savedConfig.port || port; // 修改服务器端口
}
mainWindow = new BrowserWindow({
minWidth: 800,
minHeight: 600,
title: 'ElectronDemo桌面版',
// autoHideMenuBar: true,
// frame: false, // 设置为false后为无边框窗口,即无法拖拽,拉伸窗体大小,没有菜单项
icon: path.join(__dirname, './assets/logo.ico'),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
},
});
remote.enable(mainWindow.webContents);
globalShortcut.register('CommandOrControl+M', () => {
mainWindow.maximize();
});
globalShortcut.register('CommandOrControl+T', () => {
mainWindow.unmaximize();
});
globalShortcut.register('CommandOrControl+H', () => {
mainWindow.close();
});
if (isPro) {
mainWindow.loadURL(`http://localhost:${port}/`);
console.log('Production URL:', `http://localhost:${port}/`);
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadURL('http://localhost:8000/');
console.log('development')
mainWindow.webContents.openDevTools();
}
// 解决应用启动白屏问题
mainWindow.on('ready-to-show', () => {
mainWindow.show();
mainWindow.focus();
});
mainWindow.on('close', (e) => { });
mainWindow.on('closed', () => {
mainWindow = null;
});
}
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});
}
app.on('ready', () => {
createWindow();
ipcMain.on('update-server-port', (event, newPort) => {
const server = getServer();
if (server) {
server.close(() => {
console.log(`Old server on port ${port} closed`);
// 更新端口
port = newPort;
// 启动新服务
startServer(port);
// 保存到本地文件
const configPath = path.join(app.getPath('userData'), 'config.json');
fs.writeFileSync(configPath, JSON.stringify({ port }));
});
}
});
});
try {
require('electron-reloader')(module, {});
} catch (_) { }
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
app.on('before-quit', (event) => { });
运行项目
注意package.json中的脚本命令!
启动
开发环境:
pnpm dev:启动前端项目服务
pnpm electron-dev:启动本地electron应用
生产环境:
pnpm electron:会根据你电脑系统来安装相应的可执行包
构建:
pnpm electron-build:mac:打包为mac系统可执行文件dmg
pnpm electron-build:win:打包为windows系统可执行文件.exe
最后打包成功的终端截图与文件目录结构:

1762

被折叠的 条评论
为什么被折叠?



