docs/archives/132-architecture-migration-and-session-persistence-plans/architecture-migration-guide.md
本文整合:
docs/workspace/session-store-testresults-bug-fix-2025-01-08.md(Bug 修复记录)docs/workspace/workspace-architecture-comparison-2025-01-08.md(架构对比分析)目标:给出可执行、可回滚、分阶段的迁移路线图,最终统一到 方案 C:Store + Operations。 注意:本文仅做方案设计与路线图,不执行具体迁移。
<script setup> 中访问 ComputedRef 时遗漏 .value(例如 logic.testResults?.originalResult),导致派生布尔值恒为 false,测试完成后 UI 回退到“暂无内容”。同时存在 computed getter 返回临时对象的隐患(破坏依赖追踪)。.value 遗漏概率”为第一目标,避免再出现同类 P0。testResults 数据实际存在,但 UI 不显示。复现(示例):
/#/basic/systemtestResults 值正确。testResults 为 null 时返回新对象,会破坏依赖追踪(尤其在引用比较/缓存/派生计算中)。<script setup> 内把 ComputedRef 当成普通对象访问,遗漏 .value:// 错误:logic.testResults 是 ComputedRef<TestResults | null>
const hasOriginalResult = computed(() => !!logic.testResults?.originalResult)
// 正确
const hasOriginalResult = computed(() => !!logic.testResults.value?.originalResult)
<template> 中 ref 会自动解包;但在 <script setup> 的 JS 表达式中,ref/computed 仍需 .value(或 unref()/toValue())。any/宽泛类型、推断丢失、间接层返回类型不透明)。Component (BasicSystem/UserWorkspace)
↓
useBasicWorkspaceLogic(状态代理 + 过程态 + 业务逻辑)
↓
Pinia Session Store(持久化字段,单一真源)
特点:
.value;可能引入双向 computed 与隐性写入链。Component (Context workspace)
↓
Tester composable(reactive 状态树 + 流程逻辑)
↓
(部分)Session/Preference 持久化
特点:
Component (Image workspace)
↓
Pinia Session Store(state 包装,或可迁移到 ref 拆分)
特点:
统一目标:减少“脚本里
.value漏写”这类高概率事故;让数据流更接近 Vue 3/Pinia 官方推荐的单向数据流与单一真源。
核心思路:在 Logic 层将“对象属性中的 ref/computed”扁平化,降低漏 .value 的概率。
优点:
缺点:
.value。适用结论:短期可用,但不作为最终形态。
核心思路:把 hasOriginalResult 等派生值放回 composable 内,组件尽量不要在脚本里再“二次派生”复杂 ref 树。
优点:
缺点:
适用结论:作为中期过渡方案可接受。
核心思路:
storeToRefs),少量 UI 派生 computed优点:
缺点:
适用结论:最终目标方案。
┌──────────────────────────────────────────────────────┐
│ Component (Workspace) │
│ - 直接消费 Pinia store(必要时 storeToRefs) │
│ - 少量 UI 派生 computed(或抽到 derived composable) │
│ - 触发 ops.handle* │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Operations composable(副作用/流程) │
│ - handleOptimize / handleIterate / handleTest │
│ - 负责调用 service、处理流式 token、异常与 toast │
│ - 通过 store actions 写入状态 │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Pinia Session Store(单一真源 + 持久化字段) │
│ - state: prompt/optimizedPrompt/testResults/... │
│ - actions: updatePrompt/updateTestResults/... │
│ - saveSession/restoreSession(PreferenceService) │
└──────────────────────────────────────────────────────┘
storeToRefs(store)unref()/.value<script setup> 中写派生时:
computed(() => !!unref(testResults)?.originalResult)testResults.value?.originalResultnull,由 UI 做 fallback。watch 同步 + computed setter 同步)。仅定义接口/职责,不落地具体实现细节(由 Phase 2 执行)。
export interface UseBasicWorkspaceOperationsOptions {
services: Ref<AppServices | null>
sessionStore: BasicSessionStore
optimizationMode: 'system' | 'user'
promptRecordType: PromptRecordType
}
export function useBasicWorkspaceOperations(options: UseBasicWorkspaceOperationsOptions) {
// 过程态(可返回给组件)
const isOptimizing = ref(false)
const isTestingOriginal = ref(false)
const isTestingOptimized = ref(false)
// actions(返回给组件绑定按钮事件)
const handleOptimize = async () => {}
const handleIterate = async (_payload: IteratePayload) => {}
const handleTest = async (_testVariables?: Record<string, string>) => {}
return {
isOptimizing,
isTestingOriginal,
isTestingOptimized,
handleOptimize,
handleIterate,
handleTest
}
}
时间为预估(以 1 个小团队为参照);每阶段都必须具备可回滚策略。
目标与产出
vue-tsc、ESLint 规则、关键路径测试用例。具体步骤(不执行迁移,仅准备)
storeToRefs)any/宽泛类型的限制(减少 TS “漏网”)pnpm -F @prompt-optimizer/ui test、pnpm -F @prompt-optimizer/ui build、pnpm -F @prompt-optimizer/web build 纳入关键检查。useXxxOperations 模板(异步流程/副作用)useXxxDerived 模板(派生状态聚合,可选)风险点
验收标准
回滚策略
时间预估
目标与产出
设计要点
appendOriginalToken 等)。useBasicWorkspaceOperations({ services, sessionStore, optimizationMode, promptRecordType })isTestingOriginal 等),可返回给组件。storeToRefs,避免中间层包装。useBasicWorkspaceDerived(可选)。迁移步骤(不执行,仅说明)
useBasicWorkspaceOperations.ts(与旧 useBasicWorkspaceLogic.ts 并存)。logic.handle* 切换为 ops.handle*(可通过 feature flag 或分支切换)。logic.xxx 切换为 session.xxx(必要时 storeToRefs(session))。useBasicWorkspaceLogic.ts 或仅保留为薄适配层(Phase 5 删除)。回滚方案
useBasicWorkspaceLogic 入口与组件绑定方式;若出现回归,切回旧入口(feature flag / revert commit)。验收标准(必须可量化)
/#/basic/system 与 /#/basic/user
pnpm -F @prompt-optimizer/ui test 通过pnpm -F @prompt-optimizer/ui build 通过风险点
时间预估
目标与产出
特殊考虑(reactive 状态树)
整合方案建议
useContextWorkspaceOperations:对外暴露 handleTest/handleOptimize 等useConversationTester(内部实现,可保留 reactive 状态树)storeToRefs)。回滚策略
验收标准
时间预估
目标与产出
useImageGeneration 等)作为 operations 的内部实现或依赖。对齐步骤(不执行,仅说明)
useImageWorkspaceOperations(统一 handleGenerate/handleIterate/handleTest 等命名风格)。验收标准
时间预估
目标与产出
清理项
useBasicWorkspaceLogic.ts(或降级为薄适配层后再移除)。性能优化建议(token streaming)
appendOriginalResultToken(token) / appendOptimizedResultToken(token)testResults = { ...testResults, field: field + token } 的对象拷贝deep: true watch(能用显式 action 则不用 watch)。验收标准
回滚策略
时间预估
每迁移一个 workspace/模式,按此 checklist 执行。
设计检查
组件消费检查
storeToRefs?(禁止裸解构 store)<script setup> 内所有 ref/computed 是否通过 .value/unref() 访问?回归检查
pnpm -F @prompt-optimizer/ui test / build 通过旧(高风险点:对象属性 ref)
store → useBasicWorkspaceLogic (returns { testResults: ComputedRef }) → component
↑
容易漏 .value
新(目标态)
component → operations → store (single source of truth)
↘︎ (read) ↗︎ (write)
| 场景 | 推荐写法 | 不推荐写法 | 原因 |
|---|---|---|---|
| 读取 store 字段 | session.testResults | const { testResults } = session | 裸解构会丢响应式 |
| 解构 store 字段 | const { testResults } = storeToRefs(session) | const { testResults } = session | Pinia 推荐 |
| 脚本里读取 Ref | unref(testResults)?.x / testResults.value?.x | testResults?.x | <script setup> 不会按你期望“自动解包对象属性 ref” |
| computed 默认值 | UI 层做 fallback | getter 返回新对象 | 临时对象破坏追踪/缓存 |
unref()/.value,或把派生值放到 composable 内返回。storeToRefs。