feat: 创建 Tauri API 层替换 Electron IPC 调用
- 创建 tauri-api.ts 封装所有 Tauri invoke 调用 - 保持与 Electron API 相同的接口,确保代码兼容性 - 添加类型声明文件 vite-env.d.ts - 在 main.tsx 中导入 Tauri API
This commit is contained in:
244
TAURI_MIGRATION_PLAN.md
Normal file
244
TAURI_MIGRATION_PLAN.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# Tauri 重构计划
|
||||
|
||||
## 项目概述
|
||||
|
||||
将 CC Switch 从 Electron 框架迁移到 Tauri,以大幅减少应用体积并提升性能。
|
||||
|
||||
### 目标收益
|
||||
|
||||
- **体积优化**: 77MB → ~8MB (减少 90%)
|
||||
- **内存占用**: 减少 60-70%
|
||||
- **启动速度**: 提升 3-5 倍
|
||||
- **安全性**: Rust 内存安全 + 细粒度权限控制
|
||||
|
||||
## 技术栈对比
|
||||
|
||||
| 技术层 | Electron (当前) | Tauri (目标) |
|
||||
| -------- | -------------------- | ------------------------- |
|
||||
| 后端 | Node.js + TypeScript | Rust |
|
||||
| 前端 | React + TypeScript | React + TypeScript (不变) |
|
||||
| IPC | Electron IPC | Tauri Commands |
|
||||
| 文件操作 | Node.js fs | Rust std::fs |
|
||||
| 配置存储 | electron-store | tauri-plugin-store |
|
||||
| 打包 | electron-builder | Tauri CLI |
|
||||
| WebView | Chromium (内置) | 系统 WebView |
|
||||
|
||||
## 迁移计划
|
||||
|
||||
### Phase 1: 环境准备 (Day 1 上午)
|
||||
|
||||
- [x] 安装 Rust 开发环境
|
||||
```bash
|
||||
# Windows: 下载 rustup-init.exe
|
||||
# https://www.rust-lang.org/tools/install
|
||||
```
|
||||
- [x] 安装 Tauri CLI
|
||||
```bash
|
||||
pnpm add -g @tauri-apps/cli
|
||||
```
|
||||
- [x] 在现有项目中集成 Tauri
|
||||
|
||||
```bash
|
||||
# 安装 Tauri CLI 作为开发依赖
|
||||
pnpm add -D @tauri-apps/cli
|
||||
|
||||
# 在现有项目中初始化 Tauri
|
||||
pnpm tauri init
|
||||
|
||||
# 安装 Tauri API 包
|
||||
pnpm add @tauri-apps/api
|
||||
```
|
||||
|
||||
### Phase 2: 项目结构调整 (Day 1 下午)
|
||||
|
||||
- [x] 创建 Tauri 项目配置
|
||||
- `src-tauri/` - Rust 后端代码
|
||||
- `src-tauri/tauri.conf.json` - Tauri 配置
|
||||
- `src-tauri/Cargo.toml` - Rust 依赖管理
|
||||
- [x] 迁移前端构建配置
|
||||
- 调整 Vite 配置适配 Tauri ✅
|
||||
- 更新 package.json scripts ✅
|
||||
- [x] 配置应用图标和元数据
|
||||
|
||||
### Phase 3: 后端功能迁移 (Day 2)
|
||||
|
||||
#### 3.1 核心功能模块 (上午)
|
||||
|
||||
- [x] **配置文件管理** (`src-tauri/src/config.rs`)
|
||||
- 读取 ~/.claude/settings.json
|
||||
- 写入配置文件
|
||||
- 备份/恢复配置
|
||||
- [x] **供应商管理** (`src-tauri/src/provider.rs`)
|
||||
- 供应商列表的增删改查
|
||||
- 供应商配置切换逻辑
|
||||
- 配置文件命名规则 (settings-{name}.json)
|
||||
|
||||
#### 3.2 Tauri Commands 实现 (下午)
|
||||
|
||||
```rust
|
||||
// 需要实现的命令列表 - 已完成
|
||||
#[tauri::command]
|
||||
async fn get_providers() -> Result<HashMap<String, Provider>, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_current_provider() -> Result<String, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn add_provider(provider: Provider) -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn update_provider(provider: Provider) -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn delete_provider(id: String) -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn switch_provider(id: String) -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn import_default_config() -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_claude_config_status() -> Result<ConfigStatus, String>
|
||||
```
|
||||
|
||||
#### 3.3 数据存储 (`src-tauri/src/store.rs`)
|
||||
|
||||
- [x] 使用 tauri-plugin-store 替代 electron-store
|
||||
- [x] 迁移配置存储逻辑 (~/.cc-switch/config.json)
|
||||
|
||||
### Phase 4: 前端适配 (Day 2 傍晚)
|
||||
|
||||
#### 4.1 API 层重构
|
||||
|
||||
- [ ] 创建 `src/lib/tauri-api.ts`
|
||||
- 替换 Electron IPC 调用为 Tauri invoke
|
||||
- 保持 API 接口一致,减少组件改动
|
||||
|
||||
```typescript
|
||||
// 示例:迁移前后对比
|
||||
// Electron (旧)
|
||||
window.electronAPI.getProviders();
|
||||
|
||||
// Tauri (新)
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
invoke("get_providers");
|
||||
```
|
||||
|
||||
#### 4.2 最小化前端改动
|
||||
|
||||
- [ ] 更新 preload 桥接逻辑
|
||||
- [ ] 调整窗口控制相关代码
|
||||
- [ ] 处理文件路径差异
|
||||
|
||||
### Phase 5: 测试与优化 (Day 3 上午)
|
||||
|
||||
#### 5.1 功能测试清单
|
||||
|
||||
- [ ] 供应商列表显示
|
||||
- [ ] 添加新供应商
|
||||
- [ ] 编辑供应商信息
|
||||
- [ ] 删除供应商
|
||||
- [ ] 切换供应商配置
|
||||
- [ ] 导入默认配置
|
||||
- [ ] 预设模板功能
|
||||
- [ ] API Key 快速输入
|
||||
|
||||
#### 5.2 跨平台测试
|
||||
|
||||
- [ ] Windows 10/11 测试
|
||||
- [ ] 不考虑 Windows 7/8 兼容性
|
||||
- [ ] macOS 测试 (如有条件)
|
||||
- [ ] Linux 测试 (如有条件)
|
||||
|
||||
#### 5.3 性能优化
|
||||
|
||||
- [ ] Rust 代码优化 (release 模式)
|
||||
- [ ] 减少不必要的文件 I/O
|
||||
- [ ] 优化启动加载流程
|
||||
|
||||
### Phase 6: 构建与发布 (Day 3 下午)
|
||||
|
||||
#### 6.1 构建配置
|
||||
|
||||
- [ ] 配置 GitHub Actions CI/CD
|
||||
- [ ] 设置代码签名 (Windows/macOS)
|
||||
- [ ] 配置自动更新机制
|
||||
|
||||
#### 6.2 打包发布
|
||||
|
||||
- [ ] Windows NSIS 安装包
|
||||
- [ ] Windows 便携版 (portable)
|
||||
- [ ] macOS .app 包
|
||||
- [ ] Linux AppImage
|
||||
|
||||
#### 6.3 版本发布
|
||||
|
||||
- [ ] 创建 3.0.0-beta.1 预发布
|
||||
- [ ] 编写迁移说明文档
|
||||
- [ ] 更新 README.md
|
||||
|
||||
## 风险与应对
|
||||
|
||||
### 技术风险
|
||||
|
||||
1. **Rust 学习曲线**
|
||||
|
||||
- 风险:Rust 语法相对复杂
|
||||
- 应对:专注于基础文件 I/O,使用成熟库
|
||||
|
||||
2. **WebView2 兼容性**
|
||||
|
||||
- 不需要支持旧版 Windows
|
||||
|
||||
3. **跨平台差异**
|
||||
- 风险:不同系统的文件路径处理
|
||||
- 应对:使用 Tauri API 统一处理路径
|
||||
|
||||
### 用户体验风险
|
||||
|
||||
1. **界面渲染差异**
|
||||
|
||||
- 风险:WebView 渲染可能与 Chromium 有细微差异
|
||||
- 应对:充分测试,必要时调整 CSS
|
||||
|
||||
2. **功能回归**
|
||||
- 风险:迁移过程中遗漏功能
|
||||
- 应对:严格按照测试清单验证
|
||||
|
||||
## 回滚方案
|
||||
|
||||
如果 Tauri 版本出现严重问题:
|
||||
|
||||
1. 立即从 electron-legacy 分支发布修复版本
|
||||
2. 在 GitHub Release 页面提供两个版本下载
|
||||
3. 明确标注版本差异和适用场景
|
||||
|
||||
## 时间线
|
||||
|
||||
- **Day 1**: 环境搭建 + 项目结构
|
||||
- **Day 2**: 后端迁移 + 前端适配
|
||||
- **Day 3**: 测试优化 + 构建发布
|
||||
- **Total**: 3 个工作日完成迁移
|
||||
|
||||
## 成功标准
|
||||
|
||||
- ✅ 应用体积 < 10MB
|
||||
- ✅ 冷启动时间 < 1 秒
|
||||
- ✅ 所有现有功能正常工作
|
||||
- ✅ 通过所有测试用例
|
||||
- ✅ 成功构建三平台安装包
|
||||
|
||||
## 后续优化 (可选)
|
||||
|
||||
- 添加系统托盘功能
|
||||
- 实现自动更新机制
|
||||
- 添加快捷键支持
|
||||
- 优化动画效果
|
||||
- 支持深色模式跟随系统
|
||||
|
||||
---
|
||||
|
||||
_最后更新:2024-12-23_
|
||||
_负责人:Jason Young_
|
||||
_状态:进行中 - Phase 3 已完成_
|
||||
163
src/renderer/lib/tauri-api.ts
Normal file
163
src/renderer/lib/tauri-api.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import { Provider } from '../../shared/types';
|
||||
|
||||
// 定义配置状态类型
|
||||
interface ConfigStatus {
|
||||
exists: boolean;
|
||||
path: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// 定义导入结果类型
|
||||
interface ImportResult {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
// Tauri API 封装,保持与 Electron API 相同的接口
|
||||
export const tauriAPI = {
|
||||
// 获取所有供应商
|
||||
getProviders: async (): Promise<Record<string, Provider>> => {
|
||||
try {
|
||||
return await invoke('get_providers');
|
||||
} catch (error) {
|
||||
console.error('获取供应商列表失败:', error);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
// 获取当前供应商ID
|
||||
getCurrentProvider: async (): Promise<string> => {
|
||||
try {
|
||||
return await invoke('get_current_provider');
|
||||
} catch (error) {
|
||||
console.error('获取当前供应商失败:', error);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
// 添加供应商
|
||||
addProvider: async (provider: Provider): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('add_provider', { provider });
|
||||
} catch (error) {
|
||||
console.error('添加供应商失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 更新供应商
|
||||
updateProvider: async (provider: Provider): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('update_provider', { provider });
|
||||
} catch (error) {
|
||||
console.error('更新供应商失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 删除供应商
|
||||
deleteProvider: async (id: string): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('delete_provider', { id });
|
||||
} catch (error) {
|
||||
console.error('删除供应商失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 切换供应商
|
||||
switchProvider: async (providerId: string): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke('switch_provider', { id: providerId });
|
||||
} catch (error) {
|
||||
console.error('切换供应商失败:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// 导入当前配置为默认供应商
|
||||
importCurrentConfigAsDefault: async (): Promise<ImportResult> => {
|
||||
try {
|
||||
const success = await invoke<boolean>('import_default_config');
|
||||
return {
|
||||
success,
|
||||
message: success ? '成功导入默认配置' : '导入失败'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('导入默认配置失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: String(error)
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 获取 Claude Code 配置文件路径
|
||||
getClaudeCodeConfigPath: async (): Promise<string> => {
|
||||
try {
|
||||
return await invoke('get_claude_code_config_path');
|
||||
} catch (error) {
|
||||
console.error('获取配置路径失败:', error);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
// 获取 Claude Code 配置状态
|
||||
getClaudeConfigStatus: async (): Promise<ConfigStatus> => {
|
||||
try {
|
||||
return await invoke('get_claude_config_status');
|
||||
} catch (error) {
|
||||
console.error('获取配置状态失败:', error);
|
||||
return {
|
||||
exists: false,
|
||||
path: '',
|
||||
error: String(error)
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 打开配置文件夹
|
||||
openConfigFolder: async (): Promise<void> => {
|
||||
try {
|
||||
await invoke('open_config_folder');
|
||||
} catch (error) {
|
||||
console.error('打开配置文件夹失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 打开外部链接
|
||||
openExternal: async (url: string): Promise<void> => {
|
||||
try {
|
||||
await invoke('open_external', { url });
|
||||
} catch (error) {
|
||||
console.error('打开外部链接失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 选择配置文件(Tauri 暂不实现,保留接口兼容性)
|
||||
selectConfigFile: async (): Promise<string | null> => {
|
||||
console.warn('selectConfigFile 在 Tauri 版本中暂不支持');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建全局 API 对象,兼容现有代码
|
||||
if (typeof window !== 'undefined') {
|
||||
// 检测是否在 Tauri 环境中
|
||||
const isTauri = '__TAURI__' in window;
|
||||
|
||||
if (isTauri) {
|
||||
// 在 Tauri 环境中,将 API 绑定到 window.electronAPI
|
||||
// 保持代码兼容性,无需修改组件代码
|
||||
(window as any).electronAPI = tauriAPI;
|
||||
}
|
||||
|
||||
// 提供平台信息
|
||||
(window as any).platform = {
|
||||
isMac: navigator.platform.toLowerCase().includes('mac')
|
||||
};
|
||||
}
|
||||
|
||||
export default tauriAPI;
|
||||
@@ -2,12 +2,8 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
platform?: { isMac?: boolean }
|
||||
}
|
||||
}
|
||||
// 导入 Tauri API(自动绑定到 window.electronAPI)
|
||||
import './lib/tauri-api'
|
||||
|
||||
// 根据平台添加 body class,便于平台特定样式
|
||||
if (window.platform?.isMac) {
|
||||
|
||||
39
src/renderer/vite-env.d.ts
vendored
Normal file
39
src/renderer/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { Provider } from './shared/types';
|
||||
|
||||
interface ImportResult {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface ConfigStatus {
|
||||
exists: boolean;
|
||||
path: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: {
|
||||
getProviders: () => Promise<Record<string, Provider>>;
|
||||
getCurrentProvider: () => Promise<string>;
|
||||
addProvider: (provider: Provider) => Promise<boolean>;
|
||||
deleteProvider: (id: string) => Promise<boolean>;
|
||||
updateProvider: (provider: Provider) => Promise<boolean>;
|
||||
switchProvider: (providerId: string) => Promise<boolean>;
|
||||
importCurrentConfigAsDefault: () => Promise<ImportResult>;
|
||||
getClaudeCodeConfigPath: () => Promise<string>;
|
||||
getClaudeConfigStatus: () => Promise<ConfigStatus>;
|
||||
selectConfigFile: () => Promise<string | null>;
|
||||
openConfigFolder: () => Promise<void>;
|
||||
openExternal: (url: string) => Promise<void>;
|
||||
};
|
||||
platform: {
|
||||
isMac: boolean;
|
||||
};
|
||||
__TAURI__?: any;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user