docs/archives/115-ipc-serialization-fixes/README.md
解决Electron应用中Vue响应式对象IPC序列化问题,以及由此引发的数据一致性问题。
📝 专注领域:本文档专注于Vue响应式对象的IPC序列化问题,其他IPC架构问题请参考112-desktop-ipc-fixes。
An object could not be cloned
原因:Vue响应式对象包含不可序列化的属性(Proxy、Symbol等),无法通过Electron IPC传递。
修改gemini模型apiKey → 其他模型(openai, deepseek等)全部消失
根本原因:ModelManager的updateData回调函数基于不完整的存储数据进行操作。
/**
* 安全序列化函数,用于清理Vue响应式对象
* 确保所有通过IPC传递的对象都是纯净的JavaScript对象
*/
function safeSerialize(obj) {
if (obj === null || obj === undefined) {
return obj;
}
// 对于基本类型,直接返回
if (typeof obj !== 'object') {
return obj;
}
try {
return JSON.parse(JSON.stringify(obj));
} catch (error) {
console.error('[IPC Serialization] Failed to serialize object:', error);
throw new Error(`Failed to serialize object for IPC: ${error.message}`);
}
}
// 模型管理相关
ipcMain.handle('model-updateModel', async (event, id, updates) => {
try {
const safeUpdates = safeSerialize(updates);
await modelManager.updateModel(id, safeUpdates);
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(error);
}
});
ipcMain.handle('model-addModel', async (event, model) => {
try {
const safeModel = safeSerialize(model);
const { key, ...config } = safeModel;
await modelManager.addModel(key, config);
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(error);
}
});
// 模板管理相关
ipcMain.handle('template-createTemplate', async (event, template) => {
try {
const safeTemplate = safeSerialize(template);
await templateManager.saveTemplate(safeTemplate);
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(error);
}
});
ipcMain.handle('template-updateTemplate', async (event, id, updates) => {
try {
const existingTemplate = await templateManager.getTemplate(id);
const safeUpdates = safeSerialize(updates);
const updatedTemplate = { ...existingTemplate, ...safeUpdates, id };
await templateManager.saveTemplate(updatedTemplate);
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(error);
}
});
// 历史记录相关
ipcMain.handle('history-addRecord', async (event, record) => {
try {
const safeRecord = safeSerialize(record);
const result = await historyManager.addRecord(safeRecord);
return createSuccessResponse(result);
} catch (error) {
return createErrorResponse(error);
}
});
ipcMain.handle('history-createNewChain', async (event, record) => {
try {
const safeRecord = safeSerialize(record);
const result = await historyManager.createNewChain(safeRecord);
return createSuccessResponse(result);
} catch (error) {
return createErrorResponse(error);
}
});
ipcMain.handle('history-addIteration', async (event, params) => {
try {
const safeParams = safeSerialize(params);
const result = await historyManager.addIteration(safeParams);
return createSuccessResponse(result);
} catch (error) {
return createErrorResponse(error);
}
});
ModelManager的updateData回调函数错误地基于可能不完整的存储数据:
// ❌ 错误的实现
(currentModels) => {
const models = currentModels || {}; // 可能不完整!
return {
...models, // 基于不完整的数据
[key]: updatedConfig
};
}
// ✅ 正确的实现
(currentModels) => {
// 使用内存中的完整模型列表作为基础
const models = { ...this.models };
// 如果存储中有数据,合并到内存状态中
if (currentModels) {
Object.assign(models, currentModels);
}
return {
...models, // 完整的模型列表
[key]: updatedConfig
};
}
所有ModelManager的数据更新方法:
Vue组件 → safeSerialize → IPC → 业务逻辑修复 → 增强的FileStorageProvider
↑ ↑ ↑
清理响应式对象 数据完整性保障 原子性操作+备份保护
在正确的层级解决对应的问题
始终基于完整的数据进行操作
// 错误:基于可能不完整的存储状态
const models = currentModels || {};
// 正确:基于内存中的完整状态
const models = { ...this.models };
if (currentModels) {
Object.assign(models, currentModels);
}
在IPC边界清理Vue响应式对象
// 在IPC处理器中统一清理
const safeData = safeSerialize(reactiveData);
describe('IPC Serialization', () => {
it('should handle Vue reactive objects', async () => {
const reactiveObj = reactive({ key: 'value', nested: { prop: 'test' } });
const serialized = safeSerialize(reactiveObj);
expect(serialized).toEqual({ key: 'value', nested: { prop: 'test' } });
expect(typeof serialized).toBe('object');
expect(serialized.constructor).toBe(Object);
});
});
describe('Data Consistency', () => {
it('should maintain complete model list when updating single model', async () => {
// 初始化完整的模型列表
const initialModels = { openai: config1, gemini: config2, deepseek: config3 };
// 更新单个模型
await modelManager.updateModel('gemini', { apiKey: 'new-key' });
// 验证其他模型没有丢失
const allModels = await modelManager.getAllModels();
expect(Object.keys(allModels)).toHaveLength(3);
expect(allModels.openai).toBeDefined();
expect(allModels.deepseek).toBeDefined();
});
});
这些修复经历了两个阶段:
最终实现了对Vue组件完全透明的IPC序列化处理,确保了Electron应用中数据操作的可靠性和一致性。
本目录包含以下文档:
112专注于:IPC架构完整性、异步接口设计、语言切换等功能性问题 115专注于:Vue响应式对象序列化、ElectronProxy层自动化处理