最佳实践
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,而不是继续在每个场景里复制粘贴。
这类复合组件通常是:
SessionHeaderComposerBarBottomPaneSessionTranscript
它们的价值在于让应用层只关心语义,而不是重复处理 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 细节