引言
版本:Xilem 0.4.0(crates.io 稳定版)
核心特性:无宏、纯 Rust 表达式 UI、Vello 矢量渲染、View 差分更新。
1. 核心概念速览
在开始写代码前,必须理解 Xilem 0.4 的两个核心逻辑,这决定了你的代码怎么写:
无宏,纯 Rust:Xilem 不使用 #[derive] 或 view!{} 宏。UI 就是 Rust 函数调用和元组组合。
闭包重建模型:
- App::new(|| …) 中的闭包是视图生成器。
- 状态变化时,这个闭包会重新运行,生成一棵全新的视图树。
- 框架会对比新旧视图树(Diff),只更新变化的部分。
- ⚠️ 陷阱:如果你把状态变量(如 let mut count = 0)写在闭包内部,每次重建闭包时,状态都会被重置!
2. 最简 HelloWorld 示例
这是 Xilem 的"骨架",包含了所有必要元素。
Cargo.toml
[package]
name = "xilem_lesson5"
version = "0.1.0"
edition = "2021"
[dependencies]
xilem = "0.4.0"
src/main.rs
use xilem::prelude::*;
fn main() {
App::new(|| text("Hello Xilem 0.4 🦀"))
.run_window("Xilem窗口", WindowSize::new(400, 250));
}
代码逐行解读:
- use xilem::prelude:😗;:导入核心 API(App, text, button, v_stack 等)。
- App::new(|| …):创建应用实例。闭包 || 是视图的入口。
- text(…):创建一个文本 View。在 Xilem 中,一切皆 View。
- .run_window(…):启动原生窗口和事件循环。
3. 进阶:正确的计数器(状态管理)
这是本课的重点。很多初学者会在这里踩坑。
❌ 错误写法(状态无法保存):
App::new(|| {
let mut count = 0; // 错误:每次视图重建,count 都会重置为 0
v_stack((
text(format!("计数:{}", count)),
button("点击 +1").on_click(move |_| count += 1),
))
})
✅ 正确写法(状态提升):
use xilem::prelude::*;
fn main() {
// 1. 状态定义在 App 闭包外部
let mut count = 0;
App::new(move || {
// 2. 使用 move 闭包,将 count 的所有权/引用捕获进视图生成器
v_stack((
text(format!("计数:{}", count)),
// 3. on_click 修改外部状态
button("点击 +1").on_click(move |_| count += 1),
))
})
.run_window("计数器", WindowSize::new(400, 280));
}
原理解析:
- 状态外置:count 存在于堆栈上(闭包外),不会随视图重建而销毁。
- Move 闭包:App::new 接收的闭包必须加上 move,以便捕获外部的 count。
- 更新流程:点击按钮 → count += 1 → 触发框架重绘 → App 闭包重新运行 → 生成新视图 → Diff 更新界面。
4. 布局与语法糖
Xilem 0.4 的布局语法非常 Rust 风格,主要使用元组来组合子元素。
垂直布局:v_stack((child1, child2)) — 子元素放在元组 () 中,最多支持 16 个。
水平布局:h_stack((child1, child2)) — 同上,水平排列。
按钮事件:button(“文本”).on_click(cb) — 链式调用绑定事件。
文本:text(“内容”) — 基础文本节点。
对比 Freya:
- Freya: v_stack().child(a).child(b)(链式调用)
- Xilem: v_stack((a, b))(元组组合,性能更高,编译时确定)
5. 本课练习题
请尝试独立完成以下练习,以巩固对"状态外置"和"元组布局"的理解。
练习 1:基础窗口
- 要求:窗口标题"第5课练习",大小 500×300,中间显示文字"Xilem练习"。
- 提示:直接复用 HelloWorld 代码,修改参数即可。
练习 2:垂直布局
- 要求:使用 v_stack 垂直排列两行文本。第一行"Rust GUI",第二行"Xilem 0.4"。窗口 400×200。
- 提示:v_stack 接收一个元组 (text1, text2)。
练习 3:减法计数器(重点)
- 要求:初始值为 10;一个按钮显示"➖ -1";点击按钮,数字减 1;窗口 400×250。
- 提示:记得把 let mut count = 10 写在 App::new 外面!
练习 4:思考题
- 题目:为什么下面的代码点击按钮数字不会变化?
App::new(|| {
let mut count = 0; // 为什么这里不行?
v_stack((
text(format!("count:{}",count)),
button("+1").on_click(move |_| count +=1)
))
})
6. 课后总结与对比
Xilem 0.4:纯 Rust 函数 + 元组 (a, b) 组合子元素;外部变量 + Move 闭包管理状态;闭包重建 + Diff 算法更新界面。
Freya 0.4(参考):链式调用 .child(a).child(b) 组合子元素;use_signal / Hooks 管理状态;响应式信号(Reactive Signals)更新界面。
下一课预告:我们将学习 h_stack 水平布局,以及如何给组件添加样式(文字颜色、字号、内边距)。
7. 练习题答案
练习 1 答案:
use xilem::prelude::*;
fn main() {
App::new(|| text("Xilem练习"))
.run_window("第5课练习", WindowSize::new(500, 300));
}
练习 2 答案:
use xilem::prelude::*;
fn main() {
App::new(|| {
v_stack((
text("Rust GUI"),
text("Xilem 0.4"),
))
})
.run_window("布局练习", WindowSize::new(400, 200));
}
练习 3 答案:
use xilem::prelude::*;
fn main() {
let mut count = 10;
App::new(move || {
v_stack((
text(format!("当前计数:{}", count)),
button("➖ -1").on_click(move |_| count -= 1),
))
})
.run_window("减法计数器", WindowSize::new(400, 250));
}
练习 4 答案:
count 定义在视图闭包内部,每次视图更新,闭包重新执行,count 会重置为 0。
解决方法:
把 let mut count = 0; 放到 App::new 的外面,并给 App::new 的闭包加上 move 关键字。
要不要我接着写第六课:h_stack水平布局 + 样式设置(文字颜色、字号、内边距)?
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/jackchuanqi/article/details/164750388




