news 2026/10/12 4:38:47

【dioxus0.7基础语法学与练】第3课:组件 Props 与组件通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【dioxus0.7基础语法学与练】第3课:组件 Props 与组件通信

学习目标

本课解决两个问题:父组件如何向子组件传递数据,以及子组件如何向父组件回传消息。

知识点1:Props 基础

组件通过函数参数接收外部数据,#[component] 宏会自动把这些参数转为 Props 结构体。

usedioxus::prelude::*;// 定义一个接收属性的组件#[component]fnGreeting(name:String,age:u32)->Element{rsx!{p{"你好,我叫 {name},今年 {age} 岁"}}}// 父组件调用时传入属性#[component]fnApp()->Element{rsx!{// 属性名和组件参数名一一对应Greeting{name:"张三".to_string(),age:25,}}}

属性类型规则

  • 属性可以是任何类型:String、u32、bool、Vec、自定义结构体等
  • 传入的值必须和参数类型完全匹配
  • 组件名必须大写开头,属性名必须小写开头

知识点2:可选属性与默认值

不是每个属性都必须传,用 Option 或 #[props(default)] 实现可选。

#[component]fnButton(label:String,#[props(default)]disabled:bool,// 默认 false#[props(default ="blue".to_string())]color:String,// 默认 "blue"#[props(into)]size:String,// into:自动调用 .into() 转换)->Element{rsx!{button{disabled:disabled,style:"color: {color};",class:"btn-{size}","{label}"}}}#[component]fnApp()->Element{rsx!{// 只传必填属性,其余用默认值Button{label:"提交".to_string()}// 覆盖部分默认值Button{label:"删除".to_string(),disabled:true,color:"red".to_string(),}// #[props(into)] 允许直接传 &str,自动转 StringButton{label:"取消".to_string(),size:"large",// 不需要 .to_string()}}}

三种可选机制对比:

机制 写法 效果
Option icon: Option 不传则为 None
#[props(default)] #[props(default)] x: bool 不传则用类型的默认值(false、0、“” 等)
#[props(default = expr)] #[props(default = 42)] x: i32 不传则用指定表达式的值
#[props(into)] #[props(into)] name: String 传值时自动调用 .into() 转换

知识点3:子元素传递 — children

组件可以像 HTML 标签一样包裹子内容,用 children 参数接收。

// children 类型是 Element,代表传入的子节点#[component]fnCard(title:String,children:Element)->Element{rsx!{div{class:"card",div{class:"card-header","{title}"}div{class:"card-body",{children}}// 用 { } 插入子元素}}}#[component]fnApp()->Element{rsx!{// 像 HTML 标签一样包裹内容Card{title:"用户信息".to_string(),p{"姓名:张三"}p{"年龄:25"}}Card{title:"订单详情".to_string(),ul{li{"商品A × 2"}li{"商品B × 1"}}}}}

知识点4:组件通信 — Signal 下传

父组件创建 Signal,通过 Props 传给子组件,子组件自动响应变化。

#[component]fnParent()->Element{// 父组件创建信号letcount=use_signal(||0);rsx!{// 把信号传给子组件Display{count:count}Controls{count:count}}}// 子组件接收 Signal 类型#[component]fnDisplay(count:Signal<i32>)->Element{rsx!{// 读取信号值,父组件修改时自动更新p{"当前值:{count}"}}}#[component]fnControls(count:Signal<i32>)->Element{rsx!{button{onclick:move|_|count+=1,"+1"}button{onclick:move|_|count-=1,"-1"}}}

数据流向:

Parent(创建 Signal)
├── Display(读取 Signal)← 自动响应变化
└── Controls(修改 Signal)→ 触发 Display更新

知识点5:组件通信 — 回调函数上抛

子组件不直接修改父组件状态,而是通过回调函数通知父组件"发生了什么"。

#[component]fnParent()->Element{letmutitems=use_signal(||vec!["苹果".to_string(),"香蕉".to_string()]);rsx!{ul{foriteminitems.iter(){// 把删除回调传给子组件ListItem{key:"{item}",text:item.clone(),on_delete:move||{// 父组件决定如何处理删除items.retain(|x|x!=item.as_str());},}}}}}// 子组件接收回调函数#[component]fnListItem(text:String,on_delete:EventHandler<()>,// 回调类型)->Element{rsx!{li{span{"{text}"}button{onclick:move|_|on_delete.call(()),// 触发回调"删除"}}}}

回调类型选择:

类型 说明 适用场景
EventHandler Dioxus 专用回调,用 .call(value) 触发 大多数场景,推荐
EventHandler<()> 不带数据的回调 只需通知"发生了",不传数据
EventHandler 带数据的回调 需要传递具体值(如索引、ID)

知识点6:带数据的回调

#[component]fnParent()->Element{letmutselected=use_signal(||String::new());rsx!{p{"选中:{selected}"}ColorPicker{on_select:move|color:String|{selected.set(color);},}}}#[component]fnColorPicker(on_select:EventHandler<String>)->Element{letcolors=vec!["红","绿","蓝"];rsx!{div{forcolorincolors{button{key:"{color}",onclick:move|_|on_select.call(color.to_string()),"{color}"}}}}}

知识点7:双向绑定模式

结合 Signal 下传和回调上抛,实现父子组件的双向数据流。

#[component]fnParent()->Element{letmuttext=use_signal(||String::from("初始文本"));rsx!{p{"父组件看到的值:{text}"}// 方式1:直接传 Signal(子组件可直接读写)Editor{text:text}}}// 子组件直接持有 Signal,可读可写#[component]fnEditor(text:Signal<String>)->Element{rsx!{input{value:"{text}",oninput:move|event|text.set(event.value()),}}}

核心规则

概念 说明
Props 组件函数参数,#[component] 自动转为结构体
#[props(default)] 属性可选,不传用类型默认值
#[props(default = expr)] 属性可选,不传用指定表达式
#[props(into)] 传值时自动 .into() 转换
children: Element 接收子元素,用 {children} 插入
Signal 下传 父组件创建 Signal,子组件接收 Signal 类型
EventHandler 子组件回调父组件,用 .call(value) 触发
双向绑定 子组件持有 Signal,直接读写父组件状态

动手试试

补全下面的代码:

usedioxus::prelude::*;fnmain(){dioxus::launch(App);}#[component]fnApp()->Element{rsx!{div{style:"padding: 20px; font-family: sans-serif;",// === 题目1:头像组件 ===// 调用 Avatar 组件,传入 name: "李四"、size: 48Avatar{// 补全属性}hr{}// === 题目2:折叠面板 ===Accordion{title:"什么是 Rust?".to_string(),// 补全:传入子内容 "Rust 是一门系统编程语言,注重安全性和性能。"}hr{}// === 题目3:评分组件 ===RatingDemo{}}}}// === 题目1:头像组件 ===// 要求:// - 接收 name: String(用户名)和 size: u32(头像尺寸,像素)// - 渲染一个圆形 div,宽高为 size 像素,背景色 #4A90D9// - 内部显示用户名第一个字(白色、居中、字号为 size/2)// - 提示:用 name.chars().next().unwrap_or('?') 取第一个字符#[component]fnAvatar(// 补全属性定义)->Element{// 补全渲染逻辑todo!()}// === 题目2:折叠面板 ===// 要求:// - 接收 title: String 和 children: Element// - 用 use_signal 存储 is_open,初始值 false// - 点击标题区域切换展开/收起状态// - 展开时显示 children 内容,收起时隐藏// - 标题旁显示 "▼"(展开)或 "▶"(收起)#[component]fnAccordion(// 补全属性定义)->Element{// 补全todo!()}// === 题目3:评分组件 ===// 要求:// - RatingDemo 组件用 use_signal 存储评分 rating,初始值 3// - 显示当前评分 "评分:3 / 5"// - 调用 StarRating 子组件,传入 rating 和 on_change 回调// - StarRating 组件:// - 接收 rating: u32 和 on_change: EventHandler<u32>// - 渲染 5 个星星按钮(★)// - 点击第 N 个星星,调用 on_change.call(N)// - 已选中的星星显示金色(color: gold),未选中显示灰色(color: gray)#[component]fnRatingDemo()->Element{// 补全todo!()}#[component]fnStarRating(// 补全属性定义)->Element{// 补全todo!()}

期望效果

  • 题目1:显示蓝色圆形头像,内有"李"字,尺寸 48px
  • 题目2:初始收起显示"▶ 什么是 Rust?“,点击展开显示内容和"▼”,再点击收起
  • 题目3:显示 5 个星星,前 3 个金色,点击星星可切换评分

写好了贴过来,没问题就进入第4课:事件处理与表单 👋

贴代码

Dioxus‑0.7 完整参考答案(头像、折叠面板、星级评分,含组件插槽 children、EventHandler回调)

Cargo.toml

[package] name = "dioxus-lesson" version = "0.1.0" edition = "2021" [dependencies] dioxus = "0.7"

完整可运行源码

usedioxus::prelude::*;fnmain(){dioxus::launch(App);}#[component]fnApp()->Element{rsx!{div{style:"padding: 20px; font-family: sans-serif;",// === 题目1:头像组件 ===Avatar{name:"李四".to_string(),size:48,}hr{}// === 题目2:折叠面板 ===Accordion{title:"什么是 Rust?".to_string(),children:rsx!{p{"Rust 是一门系统编程语言,注重安全性和性能。"}}}hr{}// === 题目3:评分组件 ===RatingDemo{}}}}// === 题目1:头像组件 ===#[component]fnAvatar(name:String,size:u32)->Element{letfirst_char=name.chars().next().unwrap_or('?');letfont_size=size/2;rsx!{div{style:"width:{size}px;height:{size}px;border-radius:50%;background:#4A90D9;color:white;display:flex;align-items:center;justify-content:center;font-size:{font_size}px;","{first_char}"}}}// === 题目2:折叠面板(children插槽) ===#[component]fnAccordion(title:String,children:Element)->Element{letmutis_open=use_signal(||false);rsx!{div{style:"border:1px solid #ccc;border-radius:6px;overflow:hidden;",div{style:"padding:10px;cursor:pointer;background:#f5f5f5;",onclick:move|_|is_open.toggle(),span{ifis_open(){"▼"}else{"▶"}}span{style:"margin-left:6px;","{title}"}}ifis_open(){div{style:"padding:10px;",{children}}}}}}// === 题目3:评分组件(父子通信 EventHandler) ===#[component]fnRatingDemo()->Element{letmutrating=use_signal(||3u32);rsx!{div{style:"padding:10px;",p{"评分:{rating} / 5"}StarRating{rating:rating(),on_change:move|new_val|rating.set(new_val),}}}}#[component]fnStarRating(rating:u32,on_change:EventHandler<u32>)->Element{rsx!{div{foriin1..=5{button{key:"{i}",style:"border:none;background:none;font-size:28px;cursor:pointer;color:{if i <= rating {"gold"} else {"gray"}};",onclick:move|_|on_change.call(i),"★"}}}}}

核心知识点讲解

1. 头像组件

  • chars().next() 获取字符串第一个汉字/字符;
  • flex 布局实现文字在圆形头像内水平垂直居中;
  • 尺寸通过 props 传入,字号跟随头像大小动态计算。

2. Accordion 折叠面板(children插槽)

  • children:Element 是Dioxus组件插槽,父组件可以往里面传入任意rsx内容;
  • is_open.toggle() :信号一键取反,简化展开收起逻辑;
  • 点击标题切换显示/隐藏子内容。

3. StarRating星级评分、父子组件回调通信 EventHandler

  • 父组件 RatingDemo 保存状态 rating ;
  • 子组件 StarRating 只负责UI渲染,不保存状态;
  • 点击星星 → on_change.call(i) 回调通知父组件更新评分,实现单向数据流。

关键点:

子组件只读rating,不能直接修改;状态提升到父组件,回调更新,这是Dioxus组件通信标准写法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 4:35:51

Linux 7.9部署Oracle 19c RAC全栈避坑指南

简介&#xff1a;本资源是一份面向Oracle DBA、Linux系统工程师及高可用架构学习者的实战部署指南&#xff0c;聚焦Oracle 19C RAC集群在Oracle Linux 7.9环境下的完整落地流程&#xff0c;解决多节点协同、共享存储配置、ASM磁盘组管理及集群服务启动等核心难点。文档以VMware…

作者头像 李华
网站建设 2026/10/12 4:34:43

PS5全场景改造实战:从HDMI切换、网络优化到备份恢复

“AnyPS5”是我给自家娱乐室那台PS5整合改造项目起的代号。这名字听起来像某个新主机&#xff0c;其实是一套把PS5与客厅、桌面、网络、供电、存储全部统一管理的方案。起因很简单&#xff1a;每次想玩游戏&#xff0c;都要先折腾半个小时的切换、接线、找存档和更新&#xff0…

作者头像 李华
网站建设 2026/10/12 4:32:38

具身智能创新原理(132):意图透明化与安全盾机制融合框架研究

前沿技术探索&#xff1a;TVA智能体&#xff08;简称TVA&#xff09;TVA智能体&#xff08;亦称“AI智能体视觉”&#xff09;是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统&#xff0c;也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

作者头像 李华
网站建设 2026/10/12 4:31:42

Windows全平台离线更新工具:信创/等保/产线部署刚需解析

简介&#xff1a;这是一款面向系统管理员、IT运维工程师及批量部署场景用户的Windows全平台离线补丁管理工具&#xff0c;专为解决新装系统后需耗费数小时联网下载并安装海量更新的痛点而设计。工具支持从Windows XP到8.1、Server 2003至2012 R2&#xff08;含x86/x64&#xff…

作者头像 李华
网站建设 2026/10/12 4:31:31

从零实现神经网络学习算法:前向传播、反向传播与梯度下降全解析

神经网络的入门学习&#xff0c;最难的不是搞懂模型的结构&#xff0c;而是亲手把学习算法一步步写出来。很多人调了很久的深度学习框架&#xff0c;损失函数和优化器都是一行代码调用&#xff0c;至于网络内部到底怎么根据误差更新权重、梯度是怎么从输出层传回前一层的&#…

作者头像 李华