Electron-Forge打包后Express服务器无法启动求助
问题分析
开发环境yarn start下Express服务正常运行,但yarn package打包后无法启动,将server.listen()代码移到主进程index.ts后恢复正常,核心原因是模块导入时机、webpack打包优化(Tree Shaking)、环境变量加载这三个环节在打包前后的行为差异。
具体解决方案
1. 调整模块导入顺序,避免延迟加载问题
主进程index.ts中,import { runServer } from './server';原放置在文件底部,虽然TypeScript允许此写法,但Electron Forge打包时,主进程的模块加载逻辑与开发环境的webpack-dev-server不同,延迟导入可能导致runServer函数未被正确初始化。
修改index.ts,将导入移至文件顶部:
import { app, BrowserWindow } from 'electron'; import { io } from 'socket.io-client'; import { runServer } from './server'; // 移至顶部 declare const MAIN_WINDOW_WEBPACK_ENTRY: string; declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; let mainWindow: BrowserWindow; // 剩余代码保持不变
2. 修复环境变量在打包后的加载问题
dotenv在开发环境可以读取.env文件,但打包后.env不会被包含到最终包中,导致process.env.SERVER_PORT为空。
方案一:打包时注入环境变量
修改package.json的打包脚本,通过cross-env直接注入端口变量(需先安装cross-env:yarn add cross-env -D):
"scripts": { "start": "electron-forge start", "package": "cross-env SERVER_PORT=5020 electron-forge package" }
方案二:代码中兜底默认值
在server.ts中添加默认值,避免环境变量为空:
// server.ts const SERVER_PORT = process.env.SERVER_PORT || '5020'; // 后续使用SERVER_PORT替代process.env.SERVER_PORT server.listen(SERVER_PORT, () => { console.log(`Socket.IO server running at http://localhost:${SERVER_PORT}/`); });
3. 禁用Tree Shaking,防止核心实例被优化
Webpack的Tree Shaking可能会认为server.ts中的server、io实例未被直接引用,在打包时将其剔除,导致服务无法启动。
修改主进程的webpack配置(通常是webpack.main.config.js),禁用Tree Shaking:
module.exports = { // 其他配置... optimization: { usedExports: false, minimize: false // 可选,若打包后需要调试可开启 } };
4. 显式导出核心实例(可选替代方案)
如果上述方法无效,可将server实例导出到主进程,直接在index.ts中启动服务,确保实例被正确引用:
修改server.ts:
import express from 'express'; import http from 'http'; import { Server } from 'socket.io'; const SERVER_PORT = process.env.SERVER_PORT || '5020'; const app = express(); export const server = new http.Server(app); export const io = new Server(server); export function runServer(): void { app.get('/', (req, res) => { if(req.headers.cmd !== undefined) { io.emit('command', { created: new Date().toLocaleString('pt-BR').replace(',', ''), command: atob(req.headers.cmd as string), parameters: atob(req.headers.cmddata as string) }); } res.send('SERVER RUNNING!') }); }
修改index.ts的启动逻辑:
import { server, runServer } from './server'; // 在app ready事件中: app.on('ready', () => { createWindow(); startSocketIO(); runServer(); // 初始化路由 server.listen(process.env.SERVER_PORT || '5020', () => { console.log(`Socket.IO server running at http://localhost:${process.env.SERVER_PORT || '5020'}/`); }); });
内容的提问来源于stack exchange,提问作者pes502

