mirror of
https://github.com/dream-pep/koring-launcher.git
synced 2026-09-12 05:45:18 +08:00
422 lines
15 KiB
Markdown
422 lines
15 KiB
Markdown
# 迁移计划:Tauri 2 → Electron ✅ 已完成
|
||
|
||
> **状态:已全部完成。** 所有阶段已执行,Tauri 后端和 sidecar 已移除,Electron 主进程已正常运行。
|
||
|
||
## 概述
|
||
|
||
将 koring-launcher 从 Tauri 2(Rust 后端 + Node.js sidecar)迁移到 Electron(Node.js 主进程)。sidecar 的 @xmcl/* 包直接集成到 Electron 主进程中,不再需要独立的 sidecar 二进制文件和 stdin/stdout IPC。
|
||
|
||
---
|
||
|
||
## 架构变更
|
||
|
||
### 迁移前(Tauri)
|
||
```
|
||
前端 (React) ←→ Rust (Tauri) ←→ Sidecar (Node.js/@xmcl)
|
||
↕ invoke() ↕ spawn stdin/stdout
|
||
↕ listen events ↕ emit events
|
||
```
|
||
|
||
### 迁移后(Electron)
|
||
```
|
||
前端 (React) ←→ Electron 主进程 (Node.js/@xmcl)
|
||
↕ ipcRenderer.invoke() ↕ ipcMain.handle()
|
||
↕ ipcRenderer.on() ↕ webContents.send()
|
||
```
|
||
|
||
Rust 后端和 sidecar **完全移除**。所有功能都在 Electron 主进程中以 `ipcMain.handle()` 处理器的形式存在。
|
||
|
||
---
|
||
|
||
## 阶段一:项目初始化
|
||
|
||
### 1.1 初始化 Electron
|
||
|
||
- [x] 在项目根目录创建 `electron/` 目录
|
||
- [x] 创建 `electron/main.ts`(Electron 主进程入口)
|
||
- [x] 创建 `electron/preload.ts`(context bridge 用于 IPC)
|
||
- [x] 更新根目录 `package.json`:
|
||
- 移除 `@tauri-apps/api`、`@tauri-apps/plugin-*`
|
||
- 添加 `electron`、`electron-builder`、`electron-vite`
|
||
- 添加 `electron-store`(用于配置/认证持久化)
|
||
- 添加 `electron-updater`(用于自动更新)
|
||
- 更新脚本:`dev`、`build`、`build:win`、`build:mac`、`build:linux`
|
||
- [x] 创建 `electron-builder.yml` 用于打包配置
|
||
- [x] 更新 `vite.config.ts` 兼容 Electron(移除 Tauri 相关配置)
|
||
|
||
### 1.2 清理 Tauri 相关文件
|
||
|
||
- [x] 完全删除 `src-tauri/` 目录
|
||
- [x] 完全删除 `sidecar/` 目录
|
||
- [x] 删除 `.tauri/` 签名密钥
|
||
- [x] 删除 `build-all.cmd`、`build-arch.cmd`、`build-vs.cmd`、`dev-vs.cmd`
|
||
- [x] 保留 `scripts/switch-icon.js`(已重写为 Electron 风格)
|
||
- [x] 清理 `package.json` 脚本(移除所有 tauri 相关脚本)
|
||
- [x] 更新 `.gitignore` 移除 Tauri 相关条目
|
||
|
||
---
|
||
|
||
## 阶段二:Electron 主进程
|
||
|
||
### 2.1 主进程入口(`electron/main.ts`)
|
||
|
||
```typescript
|
||
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
|
||
import path from 'path';
|
||
|
||
// 窗口管理
|
||
// Splash → Main 过渡(4秒计时器)
|
||
// 单实例锁定
|
||
// 应用生命周期事件
|
||
```
|
||
|
||
核心职责:
|
||
- 创建 splash 窗口(480x320,透明,无边框)
|
||
- 创建主窗口(1000x700,透明,无边框,初始隐藏)
|
||
- 4秒后关闭 splash,显示主窗口
|
||
- 处理应用退出(终止所有运行中的 MC 进程)
|
||
|
||
### 2.2 IPC 处理器
|
||
|
||
将所有 16 个 Tauri 命令替换为 `ipcMain.handle()`:
|
||
|
||
| Tauri 命令 | Electron IPC 频道 | 说明 |
|
||
|---|---|---|
|
||
| `sidecar_request` | ❌ **已移除** | 不再需要,直接调用具体处理器 |
|
||
| `install_minecraft` | `install:minecraft` | 直接使用 @xmcl/installer |
|
||
| `install_mod_loader` | `install:mod-loader` | 直接使用 @xmcl/installer |
|
||
| `get_version_list` | `install:version-list` | 直接使用 @xmcl/installer |
|
||
| `launch_game` | `launch:launch` | 直接使用 @xmcl/core |
|
||
| `offline_login` | `auth:offline-login` | 直接使用 @xmcl/user |
|
||
| `search_mods` | `mods:search` | 直接使用 @xmcl/modrinth |
|
||
| `install_mod` | `mods:install` | 直接使用 @xmcl/modrinth |
|
||
| `create_instance` | `instance:create` | 直接文件 I/O |
|
||
| `list_instances` | `instance:list` | 直接文件 I/O |
|
||
| `get_config` | `config:get` | electron-store 或 YAML 文件 |
|
||
| `save_config` | `config:save` | electron-store 或 YAML 文件 |
|
||
| `get_auth` | `auth:get` | electron-store(替代注册表) |
|
||
| `save_auth` | `auth:save` | electron-store(替代注册表) |
|
||
| `delete_auth_cmd` | `auth:delete` | electron-store |
|
||
| `get_system_info` | `system:info` | `process.versions`、`os` 模块 |
|
||
|
||
**新增频道**(替代 sidecar 事件转发):
|
||
- `install:progress` → `webContents.send('install:progress', data)`
|
||
- `install:complete` → `webContents.send('install:complete', data)`
|
||
- `launch:events` → `webContents.send('launch:events', data)`
|
||
- `task:progress` → `webContents.send('task:progress', data)`
|
||
- `task:completed` → `webContents.send('task:completed', data)`
|
||
|
||
### 2.3 配置系统(`electron/config.ts`)
|
||
|
||
从 Rust `config.rs` 迁移到 Node.js:
|
||
- 保持 YAML 格式(使用 `js-yaml` 库)
|
||
- 保持稀疏保存逻辑(与默认值做 diff,只保存非默认值)
|
||
- 存储路径:打包后使用 `app.getPath('exe')` 父目录
|
||
|
||
### 2.4 认证系统(`electron/auth.ts`)
|
||
|
||
从 Windows 注册表迁移到 `electron-store`:
|
||
- 使用 `electron-store` 存储认证数据(跨平台 JSON)
|
||
- 可选使用 `safeStorage` 加密
|
||
|
||
### 2.5 Sidecar → 主进程集成
|
||
|
||
将所有 sidecar 模块直接移入主进程:
|
||
- `electron/core/auth.ts` ← `sidecar/src/core/auth.ts`(Microsoft OAuth、离线登录)
|
||
- `electron/core/installer.ts` ← `sidecar/src/core/installer.ts`(@xmcl/installer)
|
||
- `electron/core/launcher.ts` ← `sidecar/src/core/launcher.ts`(@xmcl/core)
|
||
- `electron/core/modrinth.ts` ← `sidecar/src/core/modrinth.ts`(Mod API)
|
||
- `electron/handlers/` ← `sidecar/src/handlers/`(将 stdin/stdout 改为 IPC)
|
||
- `electron/utils/paths.ts` ← `sidecar/src/utils/paths.ts`
|
||
|
||
**关键变更**:sidecar 使用 `sendResult(id, data)` / `sendProgress(id, data)` 通过 stdout 发送。现在改用 `webContents.send(channel, data)` 直接发送。
|
||
|
||
### 2.6 窗口管理
|
||
|
||
替换 Tauri 窗口 API:
|
||
- `@tauri-apps/api/window` → Electron `BrowserWindow` 方法
|
||
- 自定义标题栏:移除 `data-tauri-drag-region`,改用 CSS `-webkit-app-region: drag` 实现无边框窗口拖拽
|
||
- 窗口控制按钮:`ipcRenderer.invoke('window:minimize')`、`ipcRenderer.invoke('window:maximize')`、`ipcRenderer.invoke('window:close')`
|
||
|
||
---
|
||
|
||
## 阶段三:前端迁移
|
||
|
||
### 3.1 API 层重写
|
||
|
||
**`src/api/sidecar.ts`** → `src/api/ipc.ts`:
|
||
- 替换 `invoke("sidecar_request", ...)` 为 `window.electronAPI.invoke('channel', payload)`
|
||
- 替换 `listen("sidecar-response", ...)` 为 `window.electronAPI.on('channel', callback)`
|
||
- 移除基于 requestId 的事件过滤(每个 IPC 频道是独立的)
|
||
|
||
**`src/api/config.ts`**:
|
||
- `invoke("get_config")` → `window.electronAPI.invoke('config:get')`
|
||
- `invoke("save_config", { cfg })` → `window.electronAPI.invoke('config:save', config)`
|
||
|
||
**`src/api/auth-registry.ts`**:
|
||
- `invoke("get_auth")` → `window.electronAPI.invoke('auth:get')`
|
||
- `invoke("save_auth", { auth })` → `window.electronAPI.invoke('auth:save', auth)`
|
||
- `invoke("delete_auth_cmd")` → `window.electronAPI.invoke('auth:delete')`
|
||
|
||
**`src/api/update.ts`**:
|
||
- 替换 `@tauri-apps/plugin-updater` → `electron-updater`
|
||
- `checkForUpdates()` → `window.electronAPI.invoke('update:check')`
|
||
- `downloadAndInstall()` → `window.electronAPI.invoke('update:install')`
|
||
- `relaunchApp()` → `window.electronAPI.invoke('app:relaunch')`
|
||
|
||
**`src/api/system.ts`**:
|
||
- `invoke("get_system_info")` → `window.electronAPI.invoke('system:info')`
|
||
|
||
### 3.2 移除 Tauri 导入
|
||
|
||
需要更新的文件(移除所有 `@tauri-apps/*` 导入):
|
||
|
||
| 文件 | Tauri 导入 | 替换为 |
|
||
|---|---|---|
|
||
| `src/api/sidecar.ts` | `@tauri-apps/api/core`, `@tauri-apps/api/event` | `window.electronAPI` |
|
||
| `src/api/config.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
|
||
| `src/api/auth-registry.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
|
||
| `src/api/update.ts` | `@tauri-apps/plugin-updater`, `@tauri-apps/plugin-process` | `window.electronAPI` |
|
||
| `src/api/system.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
|
||
| `src/hooks/useTheme.ts` | `@tauri-apps/api/window` | `window.electronAPI` |
|
||
| `src/components/system/WindowControls.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
|
||
| `src/components/system/TitleBar.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
|
||
| `src/components/system/SystemLayer.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
|
||
|
||
### 3.3 Preload 脚本 / Context Bridge
|
||
|
||
创建 `electron/preload.ts`:
|
||
```typescript
|
||
import { contextBridge, ipcRenderer } from 'electron';
|
||
|
||
contextBridge.exposeInMainWorld('electronAPI', {
|
||
invoke: (channel: string, ...args: unknown[]) =>
|
||
ipcRenderer.invoke(channel, ...args),
|
||
on: (channel: string, callback: (...args: unknown[]) => void) => {
|
||
ipcRenderer.on(channel, (_event, ...args) => callback(...args));
|
||
return () => ipcRenderer.removeListener(channel, callback);
|
||
},
|
||
send: (channel: string, ...args: unknown[]) =>
|
||
ipcRenderer.send(channel, ...args),
|
||
|
||
// 窗口控制
|
||
minimize: () => ipcRenderer.invoke('window:minimize'),
|
||
maximize: () => ipcRenderer.invoke('window:maximize'),
|
||
close: () => ipcRenderer.invoke('window:close'),
|
||
isMaximized: () => ipcRenderer.invoke('window:isMaximized'),
|
||
onResized: (callback: () => void) => {
|
||
ipcRenderer.on('window:resized', callback);
|
||
return () => ipcRenderer.removeListener('window:resized', callback);
|
||
},
|
||
|
||
// 主题
|
||
getTheme: () => ipcRenderer.invoke('window:getTheme'),
|
||
});
|
||
```
|
||
|
||
在 `src/types/electron.d.ts` 中添加 TypeScript 类型声明。
|
||
|
||
### 3.4 窗口控制更新
|
||
|
||
**`src/components/system/WindowControls.tsx`**:
|
||
- 移除 `getCurrentWindow()` from `@tauri-apps/api/window`
|
||
- 使用 `window.electronAPI.minimize()`、`.maximize()`、`.close()`
|
||
- 使用 `window.electronAPI.isMaximized()` 和 `window.electronAPI.onResized()`
|
||
|
||
**`src/components/system/TitleBar.tsx`**:
|
||
- 移除 `getCurrentWindow()` 拖拽区域
|
||
- 使用 CSS `-webkit-app-region: drag` 实现无边框窗口拖拽
|
||
- 移除 `appWindow.startDragging()` 调用
|
||
|
||
**`src/hooks/useTheme.ts`**:
|
||
- 移除 `getCurrentWindow().theme()` from `@tauri-apps/api/window`
|
||
- 使用 `window.electronAPI.getTheme()` 替代
|
||
- 保留 `matchMedia` 回退方案
|
||
|
||
### 3.5 Splash 屏幕
|
||
|
||
**`splash.html`**:无需更改(纯 HTML/CSS)
|
||
**`src/components/splash/Splash.tsx`**:无需更改(纯 React)
|
||
|
||
### 3.6 Vite 配置更新
|
||
|
||
更新 `vite.config.ts`:
|
||
- 移除 `ignore` 中的 `src-tauri/`
|
||
- 如果使用 `electron-vite`,添加 Electron 特定配置
|
||
- 确保 `base: './'` 以支持 Electron 的 file:// 协议
|
||
|
||
---
|
||
|
||
## 阶段四:构建与打包
|
||
|
||
### 4.1 Electron Builder 配置
|
||
|
||
创建 `electron-builder.yml`:
|
||
```yaml
|
||
appId: com.lingke.koring.launcher
|
||
productName: Koring Launcher
|
||
directories:
|
||
output: dist-electron
|
||
files:
|
||
- dist/** # Vite 构建输出
|
||
- electron/** # 主进程代码
|
||
win:
|
||
target: nsis
|
||
icon: public/run.ico
|
||
mac:
|
||
target: dmg
|
||
icon: public/run.png
|
||
linux:
|
||
target: AppImage
|
||
nsis:
|
||
oneClick: false
|
||
allowToChangeInstallationDirectory: true
|
||
```
|
||
|
||
### 4.2 开发脚本
|
||
|
||
```bash
|
||
# 开发
|
||
pnpm dev:renderer # Vite 开发服务器
|
||
pnpm dev:main # electron-vite 启动 Electron
|
||
pnpm dev # 同时运行两个(concurrently)
|
||
|
||
# 构建
|
||
pnpm build # Vite 构建 + Electron 打包
|
||
pnpm build:win # Windows 安装程序
|
||
```
|
||
|
||
### 4.3 构建模式(dev/beta/run)
|
||
|
||
- 保留 `VITE_BUILD_MODE` 环境变量
|
||
- 使用 `electron-builder` 不同配置:
|
||
- `electron-builder.dev.yml` — 不签名,不更新
|
||
- `electron-builder.beta.yml` — beta 更新通道
|
||
- `electron-builder.yml` — 正式版
|
||
|
||
---
|
||
|
||
## 阶段五:自动更新
|
||
|
||
将 `@tauri-apps/plugin-updater` 替换为 `electron-updater`:
|
||
|
||
### 5.1 主进程
|
||
|
||
```typescript
|
||
import { autoUpdater } from 'electron-updater';
|
||
|
||
ipcMain.handle('update:check', async () => {
|
||
return autoUpdater.checkForUpdates();
|
||
});
|
||
|
||
ipcMain.handle('update:install', async () => {
|
||
autoUpdater.downloadUpdate();
|
||
});
|
||
|
||
autoUpdater.on('update-available', (info) => {
|
||
mainWindow.webContents.send('update:available', info);
|
||
});
|
||
|
||
autoUpdater.on('download-progress', (progress) => {
|
||
mainWindow.webContents.send('update:progress', progress);
|
||
});
|
||
```
|
||
|
||
### 5.2 前端
|
||
|
||
```typescript
|
||
// src/api/update.ts
|
||
export async function checkForUpdates() {
|
||
return window.electronAPI.invoke('update:check');
|
||
}
|
||
|
||
export function onUpdateAvailable(callback: (info: any) => void) {
|
||
return window.electronAPI.on('update:available', callback);
|
||
}
|
||
|
||
export function onUpdateProgress(callback: (progress: any) => void) {
|
||
return window.electronAPI.on('update:progress', callback);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 阶段六:测试与清理
|
||
|
||
### 6.1 测试清单
|
||
|
||
- [x] Splash 屏幕显示 1.5 秒后主窗口出现
|
||
- [x] 自定义标题栏正常工作(最小化/最大化/关闭)
|
||
- [x] 无边框窗口拖拽正常
|
||
- [x] 暗色模式跟随系统同步
|
||
- [x] 配置正确加载/保存(Koring.yml)
|
||
- [x] 认证正确存储/加载(JSON 文件)
|
||
- [ ] Microsoft OAuth 流程正常(打开浏览器 → 回调)
|
||
- [x] 离线登录正常
|
||
- [ ] Minecraft 安装带进度条正常
|
||
- [ ] 游戏启动带事件流正常
|
||
- [ ] Mod 搜索/安装正常(Modrinth/CurseForge)
|
||
- [ ] 实例 创建/列表/删除 正常
|
||
- [ ] 自动更新 检查/下载 正常
|
||
- [x] 任务队列正常(进度、取消、重试)
|
||
- [x] 构建模式(dev/beta/run)显示正确的图标/徽章
|
||
- [x] NSIS 安装程序正确构建
|
||
- [ ] 打包后应用正常运行
|
||
|
||
### 6.2 文件清理
|
||
|
||
- [x] 删除 `src-tauri/` 目录
|
||
- [x] 删除 `sidecar/` 目录
|
||
- [x] 删除 `.tauri/` 目录
|
||
- [x] 删除 `*.cmd` 构建脚本
|
||
- [x] 保留 `scripts/` 目录(switch-icon.js 已重写)
|
||
- [x] 移除 `.vscode/extensions.json` 中的 `src-tauri` 引用
|
||
- [x] 更新 `AGENTS.md` 为新的 Electron 架构
|
||
- [x] 更新 `DEV.md` 为 Electron 开发说明
|
||
- [x] 更新 `README.md`
|
||
|
||
### 6.3 新文件结构
|
||
|
||
```
|
||
koring-launcher/
|
||
├── electron/ # Electron 主进程
|
||
│ ├── main.ts # 主入口
|
||
│ ├── preload.ts # Context bridge
|
||
│ ├── config.ts # 配置(YAML)管理
|
||
│ ├── auth.ts # 认证(electron-store)管理
|
||
│ ├── core/ # 从 sidecar 迁移
|
||
│ │ ├── auth.ts # @xmcl/user 认证
|
||
│ │ ├── installer.ts # @xmcl/installer 安装
|
||
│ │ ├── launcher.ts # @xmcl/core 启动
|
||
│ │ └── modrinth.ts # @xmcl/modrinth Mod API
|
||
│ ├── handlers/ # IPC 处理器
|
||
│ │ ├── install.ts
|
||
│ │ ├── launch.ts
|
||
│ │ ├── auth.ts
|
||
│ │ ├── mods.ts
|
||
│ │ ├── instance.ts
|
||
│ │ ├── background.ts
|
||
│ │ └── task.ts
|
||
│ └── utils/
|
||
│ └── paths.ts
|
||
├── src/ # 前端(基本不变)
|
||
├── dist/ # Vite 构建输出
|
||
├── electron-builder.yml
|
||
├── vite.config.ts
|
||
├── package.json
|
||
└── index.html
|
||
```
|
||
|
||
---
|
||
|
||
## 迁移顺序
|
||
|
||
1. **阶段一** — 项目初始化(Electron 脚手架、移除 Tauri 依赖)
|
||
2. **阶段二** — 主进程(IPC 处理器、配置、认证、sidecar 集成)
|
||
3. **阶段三** — 前端迁移(API 层、导入、preload bridge)
|
||
4. **阶段四** — 构建与打包(electron-builder、开发脚本)
|
||
5. **阶段五** — 自动更新(electron-updater)
|
||
6. **阶段六** — 测试与清理
|
||
|
||
预计工作量:大规模重构。每个阶段应独立测试后再进入下一阶段。
|