# 架构改进方案:状态管理优化 ## 问题分析 当前遇到的问题属于"响应式状态同步灾难",主要问题: 1. **状态分散**:多个 Composables 各自管理状态,难以追踪数据流 2. **响应式失效**:computed/watch 在复杂场景下失效,难以调试 3. **数据传递复杂**:props/computed/provide 多层传递,容易丢失 4. **缺乏状态快照**:无法回溯状态变化历史 5. **调试困难**:大量 console.log 散布在代码中,难以系统化 ## 改进方案 ### 1. 引入 Pinia 统一状态管理 #### 1.1 安装 Pinia ```bash npm install pinia ``` #### 1.2 创建 Store 结构 ``` stores/ ├── db-cli/ │ ├── index.ts # 主 store │ ├── connection.ts # 连接状态 │ ├── structure.ts # 表结构状态 │ ├── result.ts # 查询结果状态 │ ├── editor.ts # 编辑器状态 │ └── message.ts # 消息日志状态 └── devtools.ts # 开发工具(状态快照/回放) ``` #### 1.3 核心 Store 设计 **stores/db-cli/structure.ts** - 表结构状态管理 ```typescript import { defineStore } from 'pinia' import { ref, computed } from 'vue' export interface StructureInfo { connectionId: number database: string tableName: string dbType: 'mysql' | 'mongo' | 'redis' nodeType: string } export interface StructureData { type: string columns?: any[] database?: string table?: string // ... 其他字段 } export const useStructureStore = defineStore('structure', () => { // 状态定义 const loading = ref(false) const error = ref(null) const data = ref(null) const info = ref(null) // 计算属性(自动响应式) const hasData = computed(() => data.value !== null && info.value !== null) const isReady = computed(() => !loading.value && hasData.value) // Actions(统一的数据变更入口) async function loadStructure(params: { connectionId: number database: string tableName: string dbType: 'mysql' | 'mongo' | 'redis' nodeType: string }) { // 防止重复加载 if (loading.value) { console.warn('结构正在加载中,跳过重复请求') return } try { loading.value = true error.value = null // 验证参数 if (params.nodeType === 'connection' || params.nodeType === 'database') { info.value = { ...params, tableName: '' } data.value = null return } if (!params.tableName) { info.value = { ...params, tableName: '' } data.value = null return } // 调用后端 if (!window.go?.main?.App?.GetTableStructure) { throw new Error('Go 后端未就绪') } const result = await window.go.main.App.GetTableStructure( params.connectionId, params.database, params.tableName ) // 原子性更新(确保数据一致性) data.value = result info.value = params // 状态变更日志(开发环境) if (import.meta.env.DEV) { console.log('[StructureStore] 数据加载成功', { info: params, data: result }) } } catch (err) { const errorMessage = err instanceof Error ? err.message : '加载表结构失败' error.value = errorMessage data.value = null info.value = null if (import.meta.env.DEV) { console.error('[StructureStore] 加载失败', err) } } finally { loading.value = false } } function clear() { data.value = null info.value = null error.value = null } function reset() { loading.value = false error.value = null data.value = null info.value = null } return { // 状态 loading, error, data, info, // 计算属性 hasData, isReady, // 方法 loadStructure, clear, reset } }) ``` **stores/db-cli/index.ts** - 主 Store ```typescript import { defineStore } from 'pinia' import { useStructureStore } from './structure' import { useConnectionStore } from './connection' // ... 其他 stores // 组合 Store,提供统一访问入口 export const useDbCliStore = () => { return { structure: useStructureStore(), connection: useConnectionStore(), // ... 其他 stores } } ``` ### 2. 组件中使用 Store **views/db-cli/index.vue** ```typescript ``` ### 3. 状态调试工具 **stores/devtools.ts** - 开发工具 ```typescript import { watch } from 'vue' /** * 状态变更追踪器(仅开发环境) */ export function setupStateDebugger() { if (!import.meta.env.DEV) return // 追踪所有 store 的状态变更 const stateHistory: Array<{ timestamp: number store: string action: string oldValue: any newValue: any }> = [] return { log(store: string, action: string, oldValue: any, newValue: any) { stateHistory.push({ timestamp: Date.now(), store, action, oldValue: JSON.parse(JSON.stringify(oldValue)), newValue: JSON.parse(JSON.stringify(newValue)) }) console.group(`[${store}] ${action}`) console.log('旧值:', oldValue) console.log('新值:', newValue) console.log('历史记录:', stateHistory.slice(-10)) console.groupEnd() }, getHistory() { return stateHistory }, clearHistory() { stateHistory.length = 0 } } } ``` ### 4. 类型安全增强 **types/db-cli.ts** ```typescript // 统一类型定义 export type DbType = 'mysql' | 'mongo' | 'redis' export type NodeType = 'connection' | 'database' | 'table' | 'collection' | 'key' export interface ConnectionInfo { id: number name: string type: DbType host: string port: number database?: string } export interface StructureInfo { connectionId: number database: string tableName: string dbType: DbType nodeType: NodeType } // 严格类型检查 export function assertStructureInfo(info: unknown): asserts info is StructureInfo { if (!info || typeof info !== 'object') { throw new Error('Invalid StructureInfo') } // ... 类型检查逻辑 } ``` ### 5. 状态持久化策略 ```typescript // stores/db-cli/structure.ts import { defineStore } from 'pinia' import { useStorage } from '@vueuse/core' export const useStructureStore = defineStore('structure', () => { // 使用 localStorage 持久化(可选) const lastStructureInfo = useStorage( 'db-cli-last-structure-info', null ) // 恢复上次查看的结构 function restoreLastStructure() { if (lastStructureInfo.value) { loadStructure(lastStructureInfo.value) } } // 在 loadStructure 中保存 async function loadStructure(params: StructureInfo) { // ... 加载逻辑 info.value = params lastStructureInfo.value = params // 自动保存到 localStorage } return { /* ... */ } }) ``` ### 6. 错误边界和恢复机制 ```typescript // stores/db-cli/structure.ts export const useStructureStore = defineStore('structure', () => { const retryCount = ref(0) const maxRetries = 3 async function loadStructure(params: StructureInfo, retry = 0) { try { // ... 加载逻辑 retryCount.value = 0 // 成功后重置 } catch (err) { if (retry < maxRetries) { console.warn(`[StructureStore] 重试加载 (${retry + 1}/${maxRetries})`) await new Promise(resolve => setTimeout(resolve, 1000 * (retry + 1))) return loadStructure(params, retry + 1) } // 超过重试次数,记录错误 error.value = `加载失败(已重试 ${maxRetries} 次): ${err}` } } return { /* ... */ } }) ``` ### 7. 组件级状态同步检查 ```typescript // composables/useStateSync.ts import { watch, nextTick } from 'vue' /** * 状态同步检查器 * 确保 Store 状态和组件 props 保持同步 */ export function useStateSync( storeValue: () => T, propValue: () => T, name: string ) { if (!import.meta.env.DEV) return watch( () => storeValue(), (storeVal) => { nextTick(() => { const propVal = propValue() if (storeVal !== propVal) { console.error( `[StateSync] ${name} 不同步!`, `Store: ${JSON.stringify(storeVal)}`, `Prop: ${JSON.stringify(propVal)}` ) } }) }, { deep: true } ) } ``` ### 8. 测试策略 ```typescript // stores/db-cli/structure.test.ts import { setActivePinia, createPinia } from 'pinia' import { useStructureStore } from './structure' describe('StructureStore', () => { beforeEach(() => { setActivePinia(createPinia()) }) it('应该正确加载结构数据', async () => { const store = useStructureStore() await store.loadStructure({ connectionId: 1, database: 'test', tableName: 'users', dbType: 'mysql', nodeType: 'table' }) expect(store.loading).toBe(false) expect(store.data).not.toBeNull() expect(store.info).not.toBeNull() }) it('应该在加载失败时设置错误', async () => { // ... 测试错误处理 }) }) ``` ## 迁移步骤 1. **阶段一:引入 Pinia** - 安装依赖 - 创建基础 Store 结构 - 在主应用初始化 Pinia 2. **阶段二:迁移状态** - 先迁移 structure store(当前问题所在) - 逐步迁移其他 stores - 保持双写一段时间(Composable + Store) 3. **阶段三:清理代码** - 移除旧的 Composables - 统一使用 Store - 添加类型定义 4. **阶段四:优化和测试** - 添加状态调试工具 - 编写单元测试 - 性能优化 ## 优势总结 1. **单一数据源**:所有状态集中在 Store,避免分散 2. **自动响应式**:Pinia 自动处理响应式,无需手动 computed 3. **开发工具**:Pinia DevTools 可以可视化状态变化 4. **类型安全**:TypeScript 支持更好 5. **易于测试**:Store 可以独立测试 6. **状态持久化**:内置支持 localStorage/sessionStorage 7. **调试友好**:可以回放状态变更历史 ## 注意事项 1. **不要过度使用**:简单的局部状态仍可使用 ref/reactive 2. **避免循环依赖**:Store 之间不要相互依赖 3. **性能考虑**:大数据量使用 shallowRef 4. **SSR 兼容**:如需 SSR,注意状态初始化 ## 参考资料 - [Pinia 官方文档](https://pinia.vuejs.org/) - [Vue 3 Composition API 最佳实践](https://vuejs.org/guide/extras/composition-api-faq.html)