更新经验总结,添加多形态产品架构设计和环境感知组件的最佳实践 初始化桌面应用更新系统相关文档,设计发布与更新方案 添加导入导出架构重构文档,包含实施细节、经验和测试验证 引入 AI 自动化测试框架提升测试覆盖率和可靠性 feat(testing): 完成Electron AI自动化测试,创建测试指南 完成9个测试场景,验证端到端AI优化和Ollama集成 创建Electron MCP测试指南和最佳实践文档 总结Electron测试与浏览器测试的关键差异,建立自动化测试方法论
9.4 KiB
9.4 KiB
项目通用经验指南
本指南收录项目开发中的通用经验与最佳实践,快速解决常见问题,提升开发效率。
注意: 功能特定的经验已归档到
docs/archives/对应目录中。
📚 已归档的专项经验
- 模态框组件经验 → 106-template-management/modal-experience.md
- 布局系统经验 → 108-layout-system/experience.md
- 主题系统经验 → 109-theme-system/experience.md
- Composable架构经验 → 102-web-architecture-refactor/experience.md
- 大型架构重构经验 → 117-import-export-architecture-refactor/experience.md
🔧 通用开发规范
API 集成
// 统一 OpenAI 兼容格式
const config = {
baseURL: "https://api.provider.com/v1",
models: ["model-name"],
apiKey: import.meta.env.VITE_API_KEY // 必须使用 Vite 环境变量
};
核心原则:
- 业务逻辑与API配置分离
- 只传递用户明确配置的参数,不设默认值
- 敏感信息通过环境变量管理
错误处理
try {
await apiCall();
} catch (error) {
console.error('[Service Error]', error); // 开发日志
throw new Error('操作失败,请稍后重试'); // 用户友好提示
}
测试规范
describe("功能测试", () => {
beforeEach(() => {
testId = `test-${Date.now()}`; // 唯一标识避免冲突
});
// LLM参数测试:每个参数独立测试
it("should handle temperature parameter", async () => {
await modelManager.updateModel(configKey, {
llmParams: { temperature: 0.7 } // 只测试一个参数
});
});
});
要点:
- 使用动态唯一标识符
- 每个LLM参数创建独立测试
- 覆盖异常场景
- 正确清理测试状态
Vue 开发最佳实践
多根组件的属性继承
问题:当一个Vue组件有多个根节点时,它无法自动继承父组件传递的非prop属性(如 class),并会产生警告。
方案:
- 在
<script setup>中使用defineOptions({ inheritAttrs: false })禁用默认的属性继承行为 - 在模板中,将
v-bind="$attrs"手动绑定到你希望接收这些属性的特定根节点上
示例:
<template>
<!-- $attrs 会将 class, id 等属性应用到此组件 -->
<OutputDisplayCore v-bind="$attrs" ... />
<OutputDisplayFullscreen ... />
</template>
<script setup>
defineOptions({
inheritAttrs: false,
});
</script>
深层组件事件传播机制
问题:当全局状态变化需要通知多层级嵌套的组件时,事件传播可能中断,导致深层组件无法及时更新。
典型场景:
- 语言切换后,主界面组件更新正常,但Modal内部的组件显示旧状态
- 组件层级差异:
App.vue → ComponentA(直接引用)vsApp.vue → ComponentB → ComponentC(间接引用)
核心原因:
- v-if条件渲染:组件被销毁后ref失效,无法调用组件方法
- 事件传播断点:事件只传播到直接子组件,不会自动向下传播到深层组件
- 组件生命周期差异:不同层级的组件可能处于不同的生命周期阶段
解决方案:
-
使用v-show替代v-if:确保组件实例始终存在,ref保持有效
<!-- ❌ 问题方案:组件会被销毁 --> <Modal v-if="showModal"> <TemplateSelect ref="templateRef" /> </Modal> <!-- ✅ 推荐方案:组件始终渲染 --> <Modal v-show="showModal"> <TemplateSelect ref="templateRef" /> </Modal> -
建立完整事件传播链:从事件源到所有消费组件
// 父组件:建立事件传播 const handleGlobalStateChange = (newState) => { // 刷新直接子组件 if (directChildRef.value?.refresh) { directChildRef.value.refresh() } // 刷新深层组件(通过中间组件的暴露方法) if (intermediateRef.value?.refreshDeepChild) { intermediateRef.value.refreshDeepChild() } } // 中间组件:暴露深层组件的刷新方法 const deepChildRef = ref() const refreshDeepChild = () => { if (deepChildRef.value?.refresh) { deepChildRef.value.refresh() } } defineExpose({ refreshDeepChild }) -
统一刷新接口:所有相关组件都暴露相同的刷新方法
// 每个需要响应全局状态变化的组件都实现refresh方法 const refresh = () => { // 重新加载数据或更新状态 } defineExpose({ refresh })
最佳实践:
- 架构设计:在设计阶段考虑事件传播的完整路径
- 接口一致性:定义标准的组件刷新接口(如
refresh()方法) - 文档记录:为复杂的事件传播链建立清晰的架构图
- 测试验证:确保在所有使用场景下事件都能正确传播
适用场景:
- 全局主题切换
- 语言切换
- 用户权限变更
- 模板/配置更新
⚡ 快速问题排查
布局问题
- 检查 Flex 约束链是否完整
- 确认
min-h-0是否添加 - 验证父容器是否为
display: flex
滚动问题
- 检查是否有中间层错误的
overflow属性 - 确认高度约束是否从顶层正确传递
- 验证滚动容器是否有正确的
overflow-y: auto
组件状态同步问题
-
深层组件未更新:
- 检查是否使用了
v-if导致组件被销毁 - 确认事件传播链是否完整(父→中间→目标组件)
- 验证目标组件是否暴露了刷新方法
- 检查是否使用了
-
Modal内组件状态异常:
- 检查Modal是否使用
v-show而非v-if - 确认组件ref在Modal关闭时是否仍然有效
- 验证全局状态变化事件是否传播到Modal内部
- 检查Modal是否使用
-
组件ref调用失败:
- 确认组件是否已完成挂载(
nextTick) - 检查条件渲染是否导致组件不存在
- 验证ref绑定的组件是否暴露了对应方法
- 确认组件是否已完成挂载(
API调用问题
- 检查环境变量是否正确设置(
VITE_前缀) - 确认参数是否过度设置默认值
- 验证错误处理是否用户友好
测试失败
- 检查测试ID是否唯一
- 确认测试后是否正确清理状态
- 验证LLM参数测试是否独立
🔄 版本管理
版本同步
// package.json
{
"scripts": {
"version": "pnpm run version:sync && git add -A"
}
}
关键:使用 version 钩子而非 postversion,确保同步文件包含在版本提交中。
模板管理
- 内置模板:不可修改,不可导出
- 用户模板:可修改,导入时生成新ID
- 导入规则:跳过与内置模板ID重复的模板
🚨 关键Bug修复模式
参数透明化
// ❌ 错误:自动设置默认值
if (!config.temperature) config.temperature = 0.7;
// ✅ 正确:只使用用户配置的参数
const requestConfig = {
model: modelConfig.defaultModel,
messages: formattedMessages,
...userLlmParams // 只传递用户明确配置的参数
};
数据导入安全验证
// 白名单验证 + 类型检查
for (const [key, value] of Object.entries(importData)) {
if (!ALLOWED_KEYS.includes(key)) {
console.warn(`跳过未知配置: ${key}`);
continue;
}
if (typeof value !== 'string') {
console.warn(`跳过无效类型 ${key}: ${typeof value}`);
continue;
}
await storage.setItem(key, value);
}
国际化(i18n)键值同步
问题:[intlify] Not found 'key' in 'locale' messages 错误,通常由中英文语言包键值不同步引起。
方案:创建自动化脚本比较两个语言文件,列出差异。
📝 文档更新规范
遇到新问题或找到更好解决方案时,应及时更新此文档:
- 在对应章节添加新经验
- 更新代码示例
- 记录修复时间和问题背景
- 保持文档简洁性,避免过度详细的过程描述
记住:好的经验文档应该能让团队成员快速找到解决方案,而不是重新踩坑。
🏗️ 架构重构通用经验
大型重构策略
渐进式重构原则:
- 接口优先 - 先设计接口,再实现功能
- 分阶段执行 - 保持功能连续性,避免破坏性变更
- 测试保护 - 每个阶段都要有测试覆盖
- 文档同步 - 重构的同时更新文档
分布式架构设计
核心原则:
- 单一职责:每个服务只负责自己的数据
- 接口统一:所有服务实现相同接口
- 松耦合:服务间通过接口交互
- 可扩展:新增服务只需实现接口
存储抽象设计
避免抽象泄漏:
- 在服务层封装存储细节
- 使用逻辑键名对外暴露
- 建立清晰的抽象边界
- 文档化存储键的双重用途
AI自动化测试
MCP工具应用:
- 使用浏览器自动化验证真实用户场景
- 建立可重复执行的测试用例
- 验证架构一致性和数据完整性
- 提高测试覆盖率和可靠性