Skip to Content
指南组件与 view!

组件与 view!

Ansiq 同时保留两种组织 UI 的方式:

  • widgets 的 builder API
  • view! 的声明式树

这两者不是重复,而是服务于不同的组织方式。

继续沿用 NotesApp

上一页里,NotesApp 还是一层平铺结构。
现在我们把它拆成两个更像真实应用的部件:

  • NotesHeader
  • NotesComposer
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