Files
XCOpenCodeWeb/docs/electron-packaging.md

143 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 端口