Skip to Content
指南规模化开发

规模化开发

在前面的几页里,我们一直故意使用很小的例子。这样做的目的不是回避真实问题,而是让你先掌握 Ansiq 的主链路:

  • signal / computed / effect
  • 组件与 view!
  • focus / input / layout
  • viewport / history / patch emission

但当你的应用从一个例子变成一个长期运行的终端程序时,新的问题会出现:

  • 场景越来越多,单文件开始膨胀
  • 系统采样、网络请求、业务状态和 UI 组装混在一起
  • subtree replacement 需要保留越来越多的交互态
  • viewport 与 history 的语义开始和产品体验强耦合

这一页讨论的不是“怎么把文件夹分层得很漂亮”,而是:当应用变大时,哪些东西应该继续留在一起,哪些东西必须拆开。

一个常见的错误起点

很多终端应用一开始都像下面这样长大:

fn render(&mut self, cx: &mut ViewCtx<'_, Msg>) -> Element<Msg> { // 1. 读取系统状态 // 2. 修改业务状态 // 3. 根据状态拼装 widgets // 4. 处理各种 if/else 场景切换 }

它在很小的时候看起来没有问题,因为所有逻辑都在一个地方,改起来很快。

但一旦应用开始有:

  • 多个页面或 pane
  • 后台任务
  • 历史列表和 live viewport
  • 复杂 focus / routing
  • 真实系统采样或网络 IO

这种结构就会变得很难继续维护。最典型的症状是:

  • 改一个布局,状态机一起坏
  • 改一个 sampler,UI 也要跟着改
  • 某个组件树重排后,focus 和 scroll 变得难以解释

Ansiq 应用在规模化时应该拆成什么

Ansiq 里最稳的一种拆法是四层:

  1. app shell / scenario
  2. domain state
  3. composition / widgets
  4. services / samplers / loaders

它们的职责应该像这样分开。

1. App shell 负责“这个场景长什么样”

这一层回答的是:

  • 这个应用有几个主要区域
  • 哪个 pane 是 header / body / footer
  • 当前路由或 tab 是什么
  • 哪个区域应该获得焦点

它不应该直接做:

  • 系统命令解析
  • 网络请求
  • 复杂的业务状态转换

例如 activity_monitor 这个示例里,场景层应该只负责:

  • 顶部 Tabs
  • 中间进程表
  • 底部摘要面板

它不应该自己解析 pstopvm_stat 的原始输出。

2. Domain state 负责“应用真正知道了什么”

这一层回答的是:

  • 当前选择了哪个进程
  • 当前 tab 是 CPU 还是 Memory
  • 当前 OpenAPI 文档里选中了哪个 operation
  • 当前会话里已经完成了哪些 turn

这一层建议尽量用:

  • signal
  • computed
  • 少量 effect

而不是散落的可变字段和临时缓存。

一个很好的判断标准是:

如果去掉 UI,剩下来的状态和规则还能否被清楚描述?

如果答案是否定的,通常说明 domain state 和 UI 组装写得太紧了。

3. Composition / widgets 负责“把状态翻译成界面”

这一层是 Ansiq 文档里最容易被忽略,但在长期项目里最重要的一层。

它的作用不是发明新的业务逻辑,而是把更低阶的 widget 组合成更稳定的界面结构。

例如:

  • SessionHeader
  • ComposerBar
  • BottomPane
  • SessionTranscript

这些都不是 runtime 内核,但它们非常适合被做成复合组件,而不是在每个场景里手拼一遍。

当项目变大时,很多“看起来只是几个 Box 组合一下”的结构,最后都会演变成值得复用的 composition。

4. Services / samplers / loaders 负责“从外部世界拿数据”

这一层包括:

  • 系统监控采样
  • 文件加载
  • 远程 URL 拉取
  • OpenAPI 解析
  • 历史数据持久化

它们不应该依赖 widget,也不应该知道 layout、focus、viewport。

最理想的形态是:

  • 输入:系统环境、文件、网络
  • 输出:纯数据模型

这样场景层就可以只消费结构化数据,而不是在 render 期间临时做解析。

一个更现实的成长路径

下面这条路径比“一开始就设计完整架构”更适合 Ansiq:

第一步:从一个很小的 example 开始

先像 list_navigation 这样做一个真实但小的应用:

  • 有一块主内容
  • 有一个选中项
  • 有一个 footer

这个阶段最重要的是把:

  • signal
  • view!
  • focus 和 routing

这三件事吃透。

第二步:抽出 app shell

当你开始反复写:

  • header / body / footer
  • left pane / detail pane
  • top tabs / content / bottom info

就应该抽出 app shell,而不是继续在 render() 里堆树。

第三步:把系统相关代码移到 service/sampler

如果你的示例开始依赖:

  • ps
  • nettop
  • 远程 URL
  • 文件解析

这些逻辑都应该从场景层移出去。

这样做的收益是:

  • 测试更容易
  • UI 更稳定
  • 文档也更容易写

第四步:再考虑高级 runtime 行为

只有当你的应用真的需要时,再去做这些事情:

  • continuity keys
  • focus scope
  • viewport / history 策略
  • 更细的 subtree replacement

不要在还没有实际场景之前,就把这些机制塞进每一个小 app。

推荐目录结构

一个中等规模的 Ansiq 应用可以长成这样:

src/ app/ mod.rs shell.rs messages.rs domain/ state.rs actions.rs widgets/ session_header.rs composer_bar.rs process_table.rs services/ sampler.rs loader.rs parser.rs scenarios/ activity_monitor.rs openapi_explorer.rs

这里的关键不是目录名,而是依赖方向:

  • services 不依赖 widgets
  • widgets 不依赖 services
  • scenarios 负责把几层接起来

什么时候应该抽出新的 composition

可以用下面这个经验规则判断:

如果某一段 UI 同时满足两条,就值得抽:

  • 在两个以上场景里出现
  • 自己已经带有明确语义,而不只是“几个 Box”

例如:

  • “底部输入区”
  • “会话头部”
  • “分栏 detail explorer”
  • “摘要卡片组”

这些都比“把 Block、Paragraph、List 写得更短一点”更值得抽。

规模化时最常见的错误

把 runtime 细节当业务逻辑写

例如把:

  • focus 修复
  • continuity key
  • viewport 重锚

直接写进业务状态机里。

这通常会让业务逻辑越来越难懂。

把业务状态写进 widget 组合件

如果一个组合件开始关心:

  • 什么时候拉取数据
  • 怎么解析外部协议
  • 哪个后台任务该重试

那它通常已经不只是 widget 组合件了。

过早做“全局架构”

很多项目在还没有两个真实场景之前,就开始设计过大的抽象层。

Ansiq 更适合的做法是:

  • 先让小例子成立
  • 再抽出复用模式
  • 再沉淀进 runtime 或 widgets

这一页的真正结论

规模化开发不是“把文件分得更细”,而是要持续保持这条边界:

  • runtime 负责更新、布局、patch、session
  • app shell 负责场景结构
  • domain state 负责应用语义
  • widgets/compositions 负责界面翻译
  • services 负责外部数据

只要这条边界还清楚,Ansiq 应用就能继续长大而不失控。

最后可以回到 最佳实践 做一次收束,把这一页的结构建议压缩成日常工程规则。

Last updated on