Back to Prompt Optimizer

技术实现详解

docs/archives/111-electron-preference-architecture/implementation.md

2.10.26.2 KB
Original Source

技术实现详解

🔧 架构设计

整体架构

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   UI Components │    │  PreferenceService │    │  Storage Layer  │
│                 │    │                  │    │                 │
│ - TemplateManager│───▶│ - IPreferenceService│───▶│ - Web: useStorage│
│ - ThemeToggle   │    │ - ElectronProxy  │    │ - Electron: IPC │
│ - LanguageSwitch│    │ - usePreferences │    │ - Main: fs      │
└─────────────────┘    └──────────────────┘    └─────────────────┘

关键组件

1. IPreferenceService接口

typescript
interface IPreferenceService {
  get<T>(key: string, defaultValue: T): Promise<T>;
  set<T>(key: string, value: T): Promise<void>;
  delete(key: string): Promise<void>;
  keys(): Promise<string[]>;
  clear(): Promise<void>;
}

2. 环境检测机制

typescript
// 检测Electron API完整可用性
export function isElectronApiReady(): boolean {
  const window_any = window as any;
  const hasElectronAPI = typeof window_any.electronAPI !== 'undefined';
  const hasPreferenceApi = hasElectronAPI && 
    typeof window_any.electronAPI.preference !== 'undefined';
  return hasElectronAPI && hasPreferenceApi;
}

// 异步等待API就绪
export function waitForElectronApi(timeout = 5000): Promise<boolean> {
  return new Promise((resolve) => {
    if (isElectronApiReady()) {
      resolve(true);
      return;
    }
    
    const startTime = Date.now();
    const checkInterval = setInterval(() => {
      if (isElectronApiReady()) {
        clearInterval(checkInterval);
        resolve(true);
      } else if (Date.now() - startTime > timeout) {
        clearInterval(checkInterval);
        resolve(false);
      }
    }, 50);
  });
}

🐛 问题诊断与解决

问题1: 竞态条件错误

错误信息: Cannot read properties of undefined (reading 'preference')

根本原因:

  • Vue组件初始化时调用useTemplateManager
  • useTemplateManager立即尝试访问preferenceService
  • 但此时window.electronAPI.preference尚未完全就绪

解决方案:

  1. 延迟初始化检查: 在useAppInitializer中等待API就绪
  2. 运行时保护: 在代理服务中添加API可用性检查

问题2: API路径不匹配

错误现象: hasApi: false, hasPreferenceApi: false

根本原因:

  • preload.js暴露API在: window.electronAPI.preference
  • 代码尝试访问: window.api.preference

解决方案: 统一API路径为window.electronAPI.preference

📝 实施步骤

步骤1: 环境检测增强

文件: packages/core/src/utils/environment.ts

修改内容:

  • 新增isElectronApiReady()函数
  • 新增waitForElectronApi()函数
  • 增强API可用性检测逻辑

步骤2: 应用初始化优化

文件: packages/ui/src/composables/useAppInitializer.ts

修改内容:

typescript
if (isRunningInElectron()) {
  console.log('[AppInitializer] 检测到Electron环境,等待API就绪...');
  
  // 等待 Electron API 完全就绪
  const apiReady = await waitForElectronApi();
  if (!apiReady) {
    throw new Error('Electron API 初始化超时,请检查preload脚本是否正确加载');
  }
  
  console.log('[AppInitializer] Electron API 就绪,初始化代理服务...');
  // ... 继续初始化
}

步骤3: 代理服务保护

文件: packages/core/src/services/preference/electron-proxy.ts

修改内容:

typescript
export class ElectronPreferenceServiceProxy implements IPreferenceService {
  private ensureApiAvailable() {
    const windowAny = window as any;
    if (!windowAny?.electronAPI?.preference) {
      throw new Error('Electron API not available. Please ensure preload script is loaded and window.electronAPI.preference is accessible.');
    }
  }

  async get<T>(key: string, defaultValue: T): Promise<T> {
    this.ensureApiAvailable();
    return window.electronAPI.preference.get(key, defaultValue);
  }
  // ... 其他方法
}

步骤4: 导出更新

文件:

  • packages/core/src/index.ts
  • packages/ui/src/index.ts

修改内容: 导出新的环境检测函数

步骤5: 构建与测试

bash
# 构建core包
cd packages/core && pnpm run build

# 构建ui包  
cd packages/ui && pnpm run build

# 运行测试
pnpm run test

🔍 调试过程

调试日志分析

[isRunningInElectron] Verdict: true (via electronAPI)
[isElectronApiReady] API readiness check: {hasElectronAPI: true, hasPreferenceApi: true}
[waitForElectronApi] API already ready
[AppInitializer] Electron API 就绪,初始化代理服务...
[AppInitializer] 所有服务初始化完成

关键时序

  1. 环境检测API等待服务初始化组件挂载
  2. 确保每个步骤都完成后才进行下一步
  3. 添加超时保护防止无限等待

⚡ 性能优化

1. 快速检测

  • API就绪时立即返回,无需等待
  • 50ms检查间隔平衡响应性和性能

2. 超时保护

  • 5秒超时防止无限等待
  • 明确的错误信息指导问题排查

3. 缓存机制

  • 环境检测结果可以缓存
  • 避免重复的DOM查询

🧪 测试验证

测试结果

  • 总测试数: 262个
  • 通过数: 252个
  • 跳过数: 9个
  • 失败数: 1个(网络相关,非功能问题)

关键测试场景

  1. Electron环境启动
  2. API初始化时序
  3. 代理服务调用
  4. 错误处理机制
  5. 超时保护

🔗 相关代码文件

核心修改文件

  1. packages/core/src/utils/environment.ts - 环境检测增强
  2. packages/ui/src/composables/useAppInitializer.ts - 初始化优化
  3. packages/core/src/services/preference/electron-proxy.ts - 代理服务保护
  4. packages/core/src/index.ts - 导出更新
  5. packages/ui/src/index.ts - 导出更新

相关配置文件

  • packages/desktop/preload.js - API暴露配置
  • packages/desktop/main.js - 主进程IPC处理

实施完成日期: 2025-01-01
验证状态: ✅ 完全通过