Skip to content

长期治理:客户端失效总线取代 key 重挂载(refreshKey/actionRefreshKey)+ UI 状态分类规则 + URL 参数注册表(#2257 后续) #2269

Description

@os-zhuang

背景:#2257 修的是实例,这里要消灭的是 bug 类

framework#2604 / #2256 / #2257 / #2267 的 dogfood 过程暴露了一个结构性问题:这套 UI 把"数据刷新"和"UI 重建"绑在一起,又没有"什么状态该放哪"的成文规则。tab 重置只是它露出的角;同一根因还在持续产生:

现有代码里其实已经有正确模式的先例:notifyRelatedChanged 事件(#2256,子表保存只刷 related list、父详情原地不动)和 RelatedCountStore(模块级订阅式计数失效)。本 issue = 把先例推广成制度。

方案(三层,按投入产出排序)

P1 — 客户端失效总线,删掉两个 key(核心工作项)

把 RelatedCountStore / related-changed 的模式推广为统一的数据失效层:

  • 写路径(create/update/delete/action 成功)按 (objectName, recordId?) 精确发失效;
  • 数据消费方(详情主体 fetch、RelatedList、计数徽章、审批状态等)各自订阅、各自重取;组件树永不因数据刷新而重建;
  • 完成后直接删除 key={refreshKey} 与 key={actionRefreshKey}(及其 bump 源),crud_success 只负责 toast + 关 overlay + 发失效;
  • 兼容期:notifyRelatedChanged 收敛为总线的一个别名调用,RelatedCountStore 接到同一总线上,消灭三套并存。

收益 = 正确性(滚动/折叠/就地编辑草稿在保存后幸存)× 性能(定向重取取代全量重取)双赢。

P2 — UI 状态分类规则写进 AGENTS.md + review 检查项(半天)

状态类别 判据 归属
可寻址状态 想分享 / 刷新后想回到 / Back 应尊重 URL(?recordId= ?form= ?tab=)
用户偏好 跨记录、跨会话稳定 localStorage / 服务端偏好
真·瞬态 丢了没人心疼(hover、下拉开合) 组件内部 state

一句话规则:"任何需要在数据刷新中幸存的用户状态,不允许只活在非受控组件里。" 不开新 ADR(ADR-0054 C3 已画边界),落 AGENTS.md 约定区即可。

P3 — URL 保留参数注册表(半天,趁参数还是个位数)

app-shell 内建单一导出模块登记所有保留 query 参数:语义、push 还是 replace、谁读谁写;所有读写经它。TSDoc 即文档——AI 作者(北极星)将来消费 URL 契约时有单一学习点。

非目标 / 挂账说明

验收

  • 仓库中不再存在 key={refreshKey} / key={actionRefreshKey}(及等价的"数据刷新即重挂载"模式);
  • 浏览器验证:详情页任意 tab + 滚动位置 + 分组折叠态在「顶层编辑保存」「记录 action 执行」「子表增删改」后全部保留,数据即时更新;
  • 保存后网络面板只见定向重取(被失效的 query),无全量风暴;
  • AGENTS.md 含状态分类规则;URL 参数注册表上线且现有参数全部迁入;
  • notifyRelatedChanged / RelatedCountStore 收敛到同一失效层,无三套并存。

Refs #2257 · #2267 · #2256 · framework#2604 · ADR-0054 C3

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions