# Electron 单文件打包指南 ## 概述 将 OpenChamber 打包成单个可执行文件 (exe),用户双击即可运行服务器。 ## 核心挑战 Electron 打包后面临的最大问题:**如何启动服务器?** - 开发环境:直接用 `spawn('node', ...)` 即可,因为系统有 node - 打包后:electron 内置的是 chromium + node 融合体,没有独立的 `node` 命令 - 解决:把 node.exe 打包进 resources,用批处理脚本启动 ## 实现步骤 ### 1. 目录结构 ``` web/electron/ ├── main.js # electron 入口 ├── package.json # 打包配置 └── release/ # 输出目录 └── OpenChamber.exe ``` ### 2. main.js 关键代码 ```javascript const { app, Tray, Menu, nativeImage } = require('electron'); const path = require('path'); const fs = require('fs'); const { spawn } = require('child_process'); function startServer() { // 打包后路径 const serverPath = path.join(process.resourcesPath, 'server', 'index.js'); const cwdPath = path.join(process.resourcesPath); const execPath = path.join(process.resourcesPath, 'nodejs', 'node.exe'); // 创建批处理脚本 const batContent = `"${execPath}" "${serverPath}"`; const batPath = path.join(cwdPath, 'start-server.bat'); fs.writeFileSync(batPath, batContent); // 启动服务器 spawn('cmd.exe', ['/c', batPath], { cwd: cwdPath, stdio: 'inherit', env: { ...process.env, OPENCHAMBER_PORT: '3000', OPENCODE_SKIP_START: 'true' } }); } function createTray() { // 系统托盘图标 const iconPath = path.join(process.resourcesPath, 'public', 'favicon.png'); const tray = new Tray(nativeImage.createFromPath(iconPath)); tray.setContextMenu(Menu.buildFromTemplate([ { label: 'Open http://localhost:3000', click: () => { require('electron').shell.openExternal('http://localhost:3000'); }}, { label: 'Quit', click: () => app.quit() } ])); } app.whenReady().then(() => { startServer(); createTray(); }); ``` ### 3. package.json 打包配置 ```json { "name": "openchamber", "main": "main.js", "build": { "appId": "com.openchamber.app", "productName": "OpenChamber", "directories": { "output": "release" }, "files": [ "main.js", "package.json" ], "extraResources": [ { "from": "../dist", "to": "dist" }, { "from": "../server", "to": "server" }, { "from": "../bin", "to": "bin" }, { "from": "../public", "to": "public" }, { "from": "D:\\Program Files\\nodejs", "to": "nodejs", "filter": ["node.exe"] } ], "win": { "target": "portable" }, "portable": { "artifactName": "OpenChamber.exe" }, "asar": true } } ``` 关键点: - `extraResources` 把 dist、server、bin、public、node.exe 打包进去 - `win.target: portable` 生成单个 exe ### 4. 打包命令 ```bash cd web/electron npx electron-builder --win --x64 ``` 输出:`web/electron/release/OpenChamber.exe` (~260MB) ## 原理图 ``` OpenChamber.exe (便携版) ├── electron.exe (运行时) ├── resources/ │ ├── app.asar (electron 主代码) │ ├── dist/ (静态页面) │ ├── server/ (服务器代码) │ ├── bin/ (CLI) │ ├── public/ (静态资源) │ └── nodejs/ │ └── node.exe (Node 运行时) ``` 用户双击 exe: 1. electron 启动 2. main.js 执行 3. 创建批处理脚本调用 node.exe 运行 server/index.js 4. 服务器在 3000 端口启动 5. 系统托盘显示图标 ## 注意事项 1. **路径问题**:打包后用 `process.resourcesPath` 获取资源目录 2. **node 路径**:必须把系统 node.exe 复制到打包目录 3. **托盘图标**:需要 favicon.png,否则用空图标 4. **端口占用**:默认 3000 端口