docs/archives/111-electron-preference-architecture/experience.md
经验: Electron环境下,preload脚本的API暴露和渲染进程的组件初始化存在时序竞争
最佳实践:
// ❌ 错误做法:直接访问API
window.electronAPI.preference.get(key, defaultValue)
// ✅ 正确做法:先检查再访问
if (isElectronApiReady()) {
await window.electronAPI.preference.get(key, defaultValue)
} else {
await waitForElectronApi()
// 然后再访问
}
适用场景: 所有Electron应用的服务初始化
经验: Vue的onMounted钩子可能在服务完全就绪前触发,导致竞态条件
解决方案:
避免方式: 不要在组件挂载时立即调用可能未就绪的服务
经验: preload.js暴露的API路径必须与代码访问路径完全一致
标准模式:
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
preference: { /* API methods */ }
})
// 代码访问
window.electronAPI.preference.get()
常见错误:
electronAPI下,代码访问api// 多层检测确保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;
}
关键点:
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);
});
}
设计要点:
class ElectronPreferenceServiceProxy {
private ensureApiAvailable() {
if (!window?.electronAPI?.preference) {
throw new Error('Electron API not available');
}
}
async get<T>(key: string, defaultValue: T): Promise<T> {
this.ensureApiAvailable(); // 每次调用前检查
return window.electronAPI.preference.get(key, defaultValue);
}
}
设计原则:
// ❌ 危险:假设API已就绪
export function useTemplateManager() {
const services = inject('services')
// 这里可能在API就绪前就被调用
services.preferenceService.get('template-selection', null)
}
// ❌ 错误:路径不匹配
// preload.js: window.electronAPI.preference
// 代码访问: window.api.preference
// ❌ 危险:可能无限等待
while (!isApiReady()) {
await sleep(100) // 没有超时机制
}
console.log('[isElectronApiReady] API readiness check:', {
hasElectronAPI,
hasPreferenceApi,
});
经验: 通过服务层抽象,UI组件不需要知道底层存储实现
好处:
经验: 在Electron环境下使用代理模式封装IPC通信
优势:
经验: 使用依赖注入管理服务实例
实现方式:
// 环境适配的服务创建
if (isRunningInElectron()) {
preferenceService = new ElectronPreferenceServiceProxy()
} else {
preferenceService = createPreferenceService(storageProvider)
}
// 统一注入
provide('services', { preferenceService, ... })
// Mock Electron环境
Object.defineProperty(window, 'electronAPI', {
value: {
preference: {
get: jest.fn(),
set: jest.fn(),
}
}
})
packages/core/src/services/preference/packages/core/tests/总结日期: 2025-01-01
适用版本: Electron 37.x, Vue 3.x
经验等级: 生产环境验证