第一个应用
你的第一个 Ansiq 应用,不应该是最复杂的 showcase,也不应该是一个几乎什么都没展示的 hello world。
一个合适的教学示例应该至少包含:
- 一个
App - 一次消息流转
- 一段局部状态
- 一个输入控件
- 一段可见的 UI 更新
下面这个例子专门是为理解模型准备的。
use ansiq::prelude::*;
use ansiq::{run_app, view};
#[derive(Clone, Debug)]
enum Message {
Submit(String),
}
#[derive(Default)]
struct NotesApp {
submitted: String,
}
impl App for NotesApp {
type Message = Message;
fn render(&mut self, cx: &mut ViewCtx<'_, Self::Message>) -> Element<Self::Message> {
let draft = cx.signal(|| String::new());
let current = draft.get();
view! {
<Paragraph text={"Type and press Enter"} />
<Input
value={current.clone()}
on_change={{
let draft = draft.clone();
move |next| draft.set_if_changed(next)
}}
on_submit={|next| Some(Message::Submit(next))}
/>
<Paragraph
text={
if self.submitted.is_empty() {
"Last submit: (nothing yet)".to_string()
} else {
format!("Last submit: {}", self.submitted)
}
}
/>
}
}
fn update(&mut self, message: Self::Message, _handle: &RuntimeHandle<Self::Message>) {
let Message::Submit(next) = message;
self.submitted = next;
}
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> std::io::Result<()> {
run_app(NotesApp::default()).await
}先看结构,不要急着记语法
这个例子里有 4 个最重要的角色:
1. App
App 是 Ansiq 应用的根。它拥有两件核心职责:
render:描述当前 UIupdate:处理消息,修改状态
2. render
render 不是“直接画到 terminal 上”,而是返回一棵 Element 树。
也就是说:
- 你描述 UI 长什么样
- runtime 再负责布局、渲染、增量更新
3. cx.signal(...)
这一步用来在 render 过程中创建或获取一个稳定的响应式状态句柄。
这里它的作用非常具体:
- 当前输入框里的草稿值,由
draft这个局部 signal 持有 Input的on_change只负责修改这个局部 signal- 真正提交后的结果,才写入
self.submitted
注意它不是“普通局部变量”,也不是“直接拿来代替 self”的临时技巧。它是 runtime 和组件作用域配合的一部分。
4. update
update 处理消息,把 UI 事件变成应用状态变化。
这个例子里消息很简单:按 Enter 时提交字符串,然后把结果显示到下面。
这个例子到底发生了什么
当你输入并提交时,Ansiq 里大致会走这条链:
Input捕获键盘输入on_change更新 signal- signal 驱动局部 UI 刷新
on_submit产出Message::Submit- runtime 把消息送到
update update修改 app state- 下一轮 render 使用新的
self.submitted
所以你可以把它理解成:
- signal 适合处理“当前这一轮交互里的局部状态”
update适合处理 app 级消息与状态变化
为什么这里同时用了 self 和 signal
这是很多人第一次看 Ansiq 时最容易困惑的点。
可以先用一个简单规则理解:
self.submitted是 app 的持久业务状态draftsignal 是当前输入框的局部响应式状态
这个例子里,输入框的内容在“按下 Enter 之前”其实没有必要进入 app 的持久状态。
所以让它留在 signal 里,会比“先塞进 self,再从 self 回读到 signal”更自然。
如果后面你的应用需要:
- 在
update里验证输入 - 在多个组件之间共享当前草稿
- 或者在重启后恢复它
那时再把它提升成 app 级状态也不迟。
下一步该学什么
如果你只会照抄这个例子,还不够。
下一步要理解的是:为什么 Ansiq 要做成 runtime-first,而不是只提供 widgets。
继续阅读 核心概念。
Last updated on