WinUI Rust 学习笔记-03 项目结构
目标:系统理解一个 Windows Reactor 项目的结构——每个文件、每个核心类型 分别做什么、负责什么,以及
windows-reactor库内部是如何组织的。
1. 一个应用的两个层面
理解 Reactor 项目,可以把问题拆成两个层面:
┌─────────────────────────────────────────────────┐
│ 你的代码层(声明式) │
│ - render 函数:状态 → Element │
│ - hooks:use_state / use_effect ... │
│ - 组件函数:把 UI 拆成可复用单元 │
├─────────────────────────────────────────────────┤
│ 框架层(windows-reactor,命令式) │
│ - App / RenderCx / Element / Reconciler │
│ - 把 Element 树 diff 后应用到真实 WinUI 控件树 │
│ - 管理 Windows 窗口、消息循环、运行时 │
├─────────────────────────────────────────────────┤
│ 平台层(WinUI 3 / Windows App SDK) │
│ - 真正的控件、布局、渲染、输入事件 │
└─────────────────────────────────────────────────┘你只需要关心最上面一层:写 render 函数。下面两层由框架处理。
2. 项目里的文件分工
2.1 Cargo.toml —— 依赖清单
负责声明「这个程序依赖什么」。关键点:
[dependencies] windows-reactor:UI 库(编译期 + 运行期)。[build-dependencies] windows-reactor-setup:构建期工具,不进最终程序。edition = "2024":必需,windows-reactor用了 2024 edition 语法。
2.2 build.rs —— 构建钩子
Cargo 在编译前先编译并运行 build.rs。它的职责:
什么时候需要重新跑 build.rs? 切到别的目标架构(x86/arm64)时,构建脚本会自动重跑。
2.3 src/main.rs —— 应用逻辑
一个典型 Reactor 应用的 main.rs 结构(由简到繁):
#![windows_subsystem = "windows"]
use windows_reactor::*;
// ① 简单应用:一个 render 函数直接到底
fn app(cx: &mut RenderCx) -> Element {
// 用 hooks 读状态
let (name, set_name) = cx.use_state(String::new());
// 用 builder 拼界面
vstack((text_block(format!("Hello, {name}")), text_box().on_text_changed(...)))
.spacing(8.0)
.into()
}
fn main() -> Result<()> {
bootstrap()?; // 初始化运行时
App::new()
.title("My App")
.inner_size(800.0, 600.0)
.backdrop(Backdrop::Mica) // 亚克力/云母背景
.render(app)
}随着应用变大,推荐把 main.rs 拆成模块(见第 4 节「项目组织」)。
3. 核心类型与职责
3.1 RenderCx —— 渲染上下文
是什么:render 函数唯一的参数,一次渲染会话的上下文。
负责:提供全部 hooks(
use_state、use_ref、use_effect、use_reducer、use_resource、use_context、use_open_window等), 让你在 render 期间读取/写入应用状态。不负责:不直接操作控件。它只管理「状态与渲染」的关系。
3.2 Element —— 界面描述值
是什么:一个不可变的、可复用的「界面描述」。它不是控件实例,而是描述。
负责:携带「用什么控件、什么属性、什么子元素」的全部信息。
怎么来:builder 调用
.into()产生。怎么用:作为 render 函数的返回值、组件的返回值、容器的子元素。
一句话记忆:
Element是「图纸」,WinUI 控件是「房子」。 Reactor 拿到新图纸后,对比旧房子,只敲墙改门,而不是推倒重建。
3.3 App —— 应用外壳
是什么:顶层应用构建器。
负责:配置主窗口(标题、尺寸、背景、图标、presenter)、注册退出回调、 启动 WinUI 消息循环。
入口:
.render(f)或.run(factory)。生命周期:
render是阻塞的——它启动消息循环,直到最后一个窗口关闭, 进程才退出(on_exit回调在此刻、UI 线程上执行)。
3.4 bootstrap() —— 运行时初始化
是什么:
windows-reactor提供的顶层函数(仅框架依赖模式需要)。负责:初始化 Windows App SDK 运行时,让后续创建的 WinUI 控件可用。
时机:必须在创建任何窗口/控件前调用一次。
3.5 组件(Component / 组件函数)
是什么:可复用的「界面 + 逻辑」单元。
组件函数:普通 Rust 函数,接受
&mut RenderCx,返回Element。 Reactor 的function_component机制可给它加记忆化(memo)等能力。Component trait:更底层的接口(
render(&self, props, cx) -> Element), 供需要 props 的复杂组件使用。
4. 项目组织:从小到大的演进
4.1 单文件
所有逻辑都在 main.rs。适合学习期与小型工具。
4.2 按模块拆分(中大型推荐)
src/
├── main.rs # App 配置 + bootstrap + 入口
├── app.rs # 根 render 函数:组装整体布局
├── components/ # 自建组件
│ ├── mod.rs
│ ├── header.rs
│ └── todo_item.rs
├── state.rs # 状态类型与 reducer
└── api.rs # 网络/数据访问层模块化原则:一个组件一个文件;状态与 UI 分离;数据访问独立。
4.3 官方 gallery 应用的结构(最佳参考)
官方示例 crates/samples/reactor/gallery/ 是一个结构完整的多页面应用,值得研读:
gallery/
├── Cargo.toml
├── build.rs
└── src/
├── main.rs # 入口
├── ... # 页面组件、分类导航等5. windows-reactor 库内部结构
源码位于 crates/libs/reactor/src/。不要求你读懂全部,但知道「谁负责什么」 能帮你更快定位问题:
6. 一句话总结每个部分
7. 设计原则速记
UI 是状态的函数:
界面 = f(状态)。不手动增删控件。状态只能通过 setter 改:改完自动重渲染。
组件可复用:把重复的界面块抽成组件函数。
样式通过 builder 链设置:
.font_size()、.margin()……(后面会说明)。性能靠记忆化:
use_memo、memo组件避免无谓重渲染(后面会说明)。
小结:理解了项目结构、核心类型分工与库的内部组织。