组件与 view!
Ansiq 同时保留两种组织 UI 的方式:
- widgets 的 builder API
view!的声明式树
这两者不是重复,而是服务于不同的组织方式。
继续沿用 NotesApp
上一页里,NotesApp 还是一层平铺结构。
现在我们把它拆成两个更像真实应用的部件:
NotesHeaderNotesComposer
fn NotesHeader(text: String) -> Element<Message> {
view! {
<Paragraph text={text} />
}
}
fn NotesComposer(
value: Signal<String>,
submitted: String,
) -> Element<Message> {
view! {
<Input
value={value.get()}
on_change={{
let value = value.clone();
move |next| value.set_if_changed(next)
}}
on_submit={|next| Some(Message::Submit(next))}
/>
<Paragraph text={format!("Last submit: {}", submitted)} />
}
}
fn render(&mut self, cx: &mut ViewCtx<'_, Message>) -> Element<Message> {
let draft = cx.signal(|| String::new());
view! {
<NotesHeader text={"Type and press Enter".to_string()} />
<NotesComposer
value={draft.clone()}
submitted={self.submitted.clone()}
/>
}
}这时同一个例子已经开始体现组件边界的两个价值:
- UI 结构更清楚
- runtime scope 边界更清楚
这里也延续了前一页的同一个原则:
draft这种正在编辑中的局部值,继续留在 signal 里self.submitted这种提交后的持久结果,继续留在 app state 里
所以拆组件以后,状态边界并没有变得更模糊,反而更容易解释。
view! 适合什么
view! 更适合:
- 层级较深的 UI 树
- 结构明显强于逻辑的页面
- 你想快速看出 hierarchy 的界面
builder API 适合什么
builder API 更适合:
- 需要大量条件组合
- 你想明确控制每一步构造
- 宏会让控制流反而更绕的地方
组件在 Ansiq 里不只是语法单位
在 Ansiq 里,函数组件不仅仅是为了复用代码,它还是:
- reactive scope 的边界
- subtree replacement 的边界
- continuity contract 生效的边界
这意味着组件结构会直接影响 runtime 的更新粒度。
widgets 的定位
Ansiq 的 widgets 是 retained-tree runtime 上的低阶 primitives。
更重要的是把这两层分清:
- widgets 负责表达界面部件
- runtime 负责调度、连续性、输入路由和局部更新
下一步
继续阅读 状态、焦点与输入。
下一页我们会继续让 NotesApp 长大,加入列表和选中态,把 focus 与 continuity contract 一起放进例子里。
Last updated on