ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Xilem 0.4 基础语法学与练】第一课:从零到计数器

2026/9/11 19:35:28 拓冰建站 浏览量
【Xilem 0.4 基础语法学与练】第一课:从零到计数器 引言为什么学 XilemXilem 的核心哲学只有一句话UI f(状态)视图是状态的纯函数。你定义一个 Rust 结构体来存放状态再写一个普通函数把状态翻译成 UI 描述。状态变了函数重新执行UI 自动更新。没有 cx 上下文对象没有信号没有 use_state没有 emit(Event)——就是最朴素的函数接收数据返回结果。环境准备系统要求Rust 版本 ≥ 1.92需要 2024 edition 支持Linux 用户需额外安装系统依赖Debian/Ubuntusudo apt-get install clang libwayland-dev libxkbcommon-x11-dev libvulkan-devFedorasudo dnf install clang wayland-devel libxkbcommon-x11-devel libxcb-devel vulkan-loader-devel创建项目cargonew xilem_lesson1cdxilem_lesson1cargoaddxilem winit核心概念三层架构Xilem 内部有三层但你只需要关心最上面一层你写的代码View 层 → “我想要一个按钮和一个标签”蓝图↓ diff框架自动处理Element 层→ “按钮没变只更新标签文字”施工调度↓底层渲染Widget 层 → 用 Vello GPU 引擎画到屏幕上实际建造你只负责画蓝图——用纯 Rust 函数描述 UI 应该长什么样。状态变化时框架自动 diff 并最小化更新底层 Widget。第一个程序计数器将 src/main.rs 替换为以下代码usewinit::error::EventLoopError;usexilem::view::{Axis,text_button,flex,label};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};// ① 定义应用状态——可以是任意 Rust 类型#[derive(Default)]structCounter{num:i32,}// ② 定义视图函数——状态 → UI 描述fnapp_logic(data:mutCounter)-implWidgetViewCounteruse{flex(Axis::Vertical,// 纵向排列(label(format!({},data.num)),// 显示数字text_button(increment,|data:mutCounter|data.num1),// 按钮),)}// ③ 启动应用fnmain()-Result(),EventLoopError{letappXilem::new_simple(Counter::default(),// 初始状态app_logic,// 视图函数WindowOptions::new(我的第一个 Xilem 应用),// 窗口配置);app.run_in(EventLoop::with_user_event())?;Ok(())}运行cargorun点击按钮数字就会 1。就这么简单。逐行拆解三个关键语法点语法点 1状态是任意 Rust 类型#[derive(Default)]structCounter{num:i32,}Xilem 不限制你的状态类型。可以是结构体、元组、枚举甚至 i32。#[derive(Default)] 是为了方便创建初始值。语法点 2视图是状态的纯函数fnapp_logic(data:mutCounter)-implWidgetViewCounteruse{参数 data: mut Counter框架传入当前状态的可变引用返回值 impl WidgetView返回一个描述 UI 的 View 对象useRust 2024 edition 的精确捕获语法表示闭包不捕获任何外部环境变量语法点 3声明式布局——函数 元组flex(Axis::Vertical,// 第一个参数排列方向(// 第二个参数子元素元组label(...),text_button(...),),)flex() 是 Flexbox 布局容器Axis::Vertical 表示纵向排列还有 Axis::Horizontal子元素用元组 (a, b, c) 组合最多 12 个元素超过 12 个嵌套 flex 即可事件处理闭包直接修改状态text_button(increment,|data:mutCounter|data.num1)第一个参数按钮文字第二个参数回调闭包接收 mut Counter直接修改状态修改后框架自动重新执行 app_logicdiff 后只更新变化的部分 核心理解你不需要手动调用刷新 UI。修改状态 → 框架自动重新执行视图函数 → diff 算法只更新变化的 widget。这就是响应式。试一试练习一加减计数器在计数器的基础上添加一个decrement按钮实现加减计数。窗口中应该显示当前数字以及两个按钮一个 1一个 -1。练习二横向布局 多状态字段将状态扩展为包含姓名String和分数i32两个字段纵向显示姓名和分数下方用横向布局放置两个按钮一个得分 10一个重置分数归零、姓名恢复为新玩家。练习三进阶条件渲染在练习二的基础上根据分数显示不同的提示文字分数 ≥ 100显示 太棒了分数 ≥ 50显示 继续加油分数 50显示 开始游戏吧提示视图函数就是普通 Rust 函数if/else、match 都可以直接用。练习答案与知识点总结以下是三个练习的正确答案练习一答案fnapp_logic(data:mutCounter)-implWidgetViewCounteruse{flex(Axis::Vertical,(label(format!({},data.num)),text_button(1,|data:mutCounter|data.num1),text_button(-1,|data:mutCounter|data.num-1),),)}知识点元组中可以放任意数量的子 View每个 View 独立声明。多个 text_button 放在同一个元组里就会按 flex 指定的方向依次排列。每个按钮的闭包独立捕获 mut Counter各自修改同一个字段互不干扰。练习二答案structPlayer{name:String,score:i32,}implDefaultforPlayer{fndefault()-Self{Player{name:新玩家.to_string(),score:0,}}}fnapp_logic(data:mutPlayer)-implWidgetViewPlayeruse{flex(Axis::Vertical,(label(format!(玩家: {},data.name)),label(format!(分数: {},data.score)),flex(Axis::Horizontal,(text_button(得分 10,|data:mutPlayer|data.score10),text_button(重置,|data:mutPlayer|{data.score0;data.name新玩家.to_string();}),),),),)}知识点当业务需要的默认值不是类型的零值时比如 String 的零值是 “”但我们需要 “新玩家”不能用 #[derive(Default)]必须手动实现 Default trait。姓名和分数各自是一个 label直接放在外层 flex(Axis::Vertical, (…)) 的元组中自然纵向排列、各自独占一行。两个按钮放在内层 flex(Axis::Horizontal, (…)) 中实现横向并排。外层纵向、内层横向——嵌套 flex 是 Xilem 实现混合布局的标准方式。闭包中可以写多行逻辑最后一个表达式的值作为闭包返回值。重置闭包中最后一条语句返回 ()即 unit 类型正好匹配闭包的返回要求。练习三答案fnapp_logic(data:mutPlayer)-implWidgetViewPlayeruse{lettipifdata.score100{ 太棒了}elseifdata.score50{ 继续加油}else{ 开始游戏吧};flex(Axis::Vertical,(label(format!(玩家: {},data.name)),label(format!(分数: {},data.score)),label(tip.to_string()),flex(Axis::Horizontal,(text_button(得分 10,|data:mutPlayer|data.score10),text_button(重置,|data:mutPlayer|{data.score0;data.name新玩家.to_string();}),),),),)}知识点视图函数就是普通 Rust 函数if/else 等控制流可以直接用在 View 构建之前先算好数据再传给 View 组件。在练习二的基础上意味着保留所有已有功能姓名、分数、得分按钮、重置按钮只新增题目要求的条件渲染部分——在分数标签和按钮行之间插入一行 label(tip.to_string())。if/else 的三个分支都返回 str类型一致所以可以赋值给同一个变量 tip。如果不同分支返回不同类型的 View则需要用 one_of 做类型统一——这是后续课程会讲到的内容。本课知识清单状态用 struct Default 定义任意 Rust 类型都行框架通过 mut 引用传入视图函数视图函数签名为 fn app_logic(data: mut S) - impl WidgetView是一个纯函数输入状态、输出 UI 描述。布局用 flex(Axis, (child1, child2)) 函数 元组的方式声明式组合 Axis::Vertical 纵向、 Axis::Horizontal 横向。事件 text_button(“文字”, |data| …) 的闭包直接接收状态的可变引用修改即触发刷新。更新机制View diff——你只管改状态框架自动 diff View 树并最小化更新底层 Widget。控制流 if/else 、 match 等 Rust 原生语法直接用在视图函数中无需任何模板。下课预告第二课将覆盖WidgetView trait 的更多方法.on_click()、.enabled() 等列表渲染for_each / ViewSequence组件拆分将大视图拆成多个小函数memoize 性能优化