Skip to Content
指南最佳实践

最佳实践

Ansiq 不是一个“组件堆起来就结束了”的 UI 库。

它有一个比较强的 runtime 内核,这会带来一个直接后果:

如果你在一开始就把边界切对,项目会长得很稳。
如果边界切错,问题不会马上爆炸,但会在下面这些地方反复出现:

  • subtree replacement
  • focus 和 input routing
  • viewport / history
  • interactive widget state continuity

这一页不是一份风格建议,而是一份工程建议。

1. 从小而真实的例子开始

最好的入门路径不是 activity monitor 这种 showcase,而是:

  • 一个 List
  • 一个选中状态
  • 一个 footer
  • 少量键盘交互

这就是为什么文档里先用 list_navigation,再讲更复杂的示例。

如果你一开始就从:

  • 多 pane
  • 实时采样
  • 远程加载
  • 大量复合组件

开始,你会同时面对太多问题,很难判断 bug 是出在:

  • 自己的状态模型
  • widget 语义
  • runtime 边界

2. 用 signal 表达状态,用 effect 表达副作用

在 Ansiq 里,一个非常实用的经验规则是:

  • signal:存事实
  • computed:存派生视图
  • effect:做副作用

不要把 effect 当成“另一个 setState”。它更适合:

  • 采样任务触发
  • 同步某些外部系统
  • 日志
  • 非 UI 的 side effect

当一个值只是另一个值的函数时,优先用 computed,不要手动维护第二份状态。

3. 把应用语义和 runtime 行为分开

应用语义包括:

  • 当前选择了哪个进程
  • 当前处于哪个 tab
  • 哪个 operation 被选中
  • 哪条消息属于当前 turn

runtime 行为包括:

  • focus 在哪个节点
  • subtree replacement 后怎么保住 cursor
  • viewport 如何 reanchor
  • history 何时 commit

这两者如果混在一起,调试会很痛苦。

一个简单判断方式是:

如果你在业务 reducer 或 app state 里到处写:

  • continuity key
  • focus scope
  • invalidated region
  • reanchor plan

那通常说明 runtime 细节泄漏到了应用层。

4. continuity key 要显式、稳定、可解释

Ansiq 现在已经支持 subtree replacement 下的状态连续性,包括:

  • input cursor
  • list/table selection
  • tabs selection
  • scroll offsets

但它工作的前提是:
你要在真正需要稳定身份的地方给出明确 key。

不要依赖“刚好顺序没变”这种偶然条件。

最适合加 continuity key 的位置通常是:

  • 可重排的 sibling 列表
  • 会被条件渲染切换的 pane
  • 需要保留输入或滚动位置的区域

5. 把 viewport 和 history 视为产品设计,而不是渲染细节

很多终端应用一开始只把 viewport 当成“给我一块可画区域”。

但一旦你的 app 有:

  • 多轮对话
  • 历史滚进 scrollback
  • sticky footer
  • inline reserve

viewport 和 history 就变成产品体验的一部分了。

在 Ansiq 里,你应该尽早明确这些问题:

  • 当前内容是 live viewport 的一部分,还是历史的一部分
  • history 在 commit 后是否 reflow
  • resize 后该保留什么,不该保留什么
  • 用户滚离底部时,你要不要自动把视口拉回去

这些问题不是“最后补一下 UX”,而是架构问题。

6. narrative docs 讲模式,API docs 讲接口

一个常见错误是把所有说明都写成 API 手册。

更稳的做法是:

  • Guide 负责讲“什么时候该这么做”
  • Internals 负责讲“为什么系统这样设计”
  • API 负责讲“接口叫什么、参数是什么”

这样文档才不会一边教学,一边被细节拖死。

7. 组合件优先于场景内重复拼装

如果你发现自己在不同场景里反复写:

  • header + status + summary
  • transcript + footer
  • detail pane + schema pane

优先抽出 composition,而不是继续在每个场景里复制粘贴。

这类复合组件通常是:

  • SessionHeader
  • ComposerBar
  • BottomPane
  • SessionTranscript

它们的价值在于让应用层只关心语义,而不是重复处理 layout 细节。

8. 不要过早把所有东西塞进 runtime

Ansiq 的 runtime 已经比较强了,这会带来一个诱惑:

“既然 runtime 已经能做 subtree replacement、focus、patch,那是不是所有通用逻辑都该放进去?”

答案是否定的。

runtime 应该只负责:

  • 更新调度
  • 局部布局
  • patch emission
  • session / viewport

而不负责:

  • 业务状态建模
  • OpenAPI 解析
  • 活动监视器采样
  • 应用层 shell 文案

9. 当你在 debug 时,先问“边界错了没有”

很多 bug 表面上看像渲染问题,实际上是边界问题。

举例:

  • 一个选中项丢了
    可能不是 widget 坏了,而是 continuity key 没有设

  • 一个 footer 留下残影
    可能不是 border 画错了,而是 viewport 重锚或 full redraw 边界错了

  • 一个 pane 重排后 focus 乱了
    可能不是 focus 系统坏了,而是 subtree replacement 的身份关系变了

这也是为什么 Ansiq 文档会把 Internals 和 Guide 分开:
只看 API,你很难真正 debug 这类问题。

最后给一份真正有用的短清单

如果你只想记住最重要的几条,就记下面这些:

  • 从小而真实的例子开始
  • signal / computed / effect 明确区分状态、派生和副作用
  • 把 app state 和 runtime 行为分开
  • 对需要稳定身份的节点显式加 continuity key
  • 把 viewport / history 当成产品语义来设计
  • 优先抽复合组件,而不是在场景里重复拼装
  • 出问题时,先检查边界,再检查 widget 细节
Last updated on