当前位置: 首页 > news >正文

Dioxus 的 `rsx!` 语法:如果你会 React,上手确实特别快

前言

上一篇我们把第一个 Dioxus 项目跑起来了。

这一篇先不讲 CLI,也不碰状态管理,只聊一件事:rsx!到底该怎么理解。

我一开始对它的判断也很简单:这不就是 Rust 版 JSX 吗。

但真写了输入框、按钮、列表、条件分支之后,我发现这个理解只能对一半。

它确实像 JSX,但更准确一点说,它是一套长得像 HTML 的 Rust UI 语法

这个认知早点拧过来,后面学组件、路由、Signals 都会轻松不少。要是没拧过来,你就会一直下意识去找三元表达式、event.target.value,越写越别扭。

1.rsx!到底是什么

先说结论:rsx!不是模板引擎,它就是一个 Rust 宏。

官方文档在 Dioxus 0.7 里说得很直接:RSX 是用来构建 Dioxus UI 的语法,底层会被过程宏展开成 Rust 代码,不是单独的模板文件。

所以你看到的:

rsx!{h1{"Welcome to Dioxus!"}p{"Hello, {name}"}}

它不是“在 Rust 里塞了一段 HTML”,更像是“用更短的方式描述一棵 UI 树”。

这也是它和传统模板系统差别最大的地方:

  • 模板系统通常有自己的一套语法规则
  • rsx!直接活在 Rust 语法环境里
  • 花括号里的内容不是特殊模板语法,而是普通 Rust 表达式

举个例子:

usedioxus::prelude::*;fnmain(){dioxus::launch(App);}#[component]fnApp()->Element{letname="Dioxus";rsx!{div{h1{"你好,{name}"}p{"这段 UI 不是模板文件,而是 Rust 代码的一部分。"}}}}

这段代码真正重要的,不是“标签看起来像 HTML”,而是你已经在一个 Rust 函数里把 UI 写完了。

2. 为什么 React 开发者会觉得它很眼熟

如果你写过 React,第一眼看到rsx!,大概率会觉得挺顺。

因为它和 JSX 有几个很像的地方。

2.1 都是声明式 UI

你不需要一行行去创建节点、设置文本、挂事件。你只描述“页面应该长什么样”,状态变化后框架帮你更新。

2.2 都是标签结构 + 属性 + 插值

举个例子,下面这段 Dioxus 代码基本不用翻译:

rsx!{section{class:"hero",h1{"欢迎回来,{user_name}"}p{"今天继续改你的 Dioxus 页面。"}}}

你能一眼看出来:

  • sectionh1p是元素
  • class是属性
  • {user_name}是动态插值

2.3 事件也是贴在元素上

button{onclick:move|_|println!("clicked"),"点我"}

这和 React 的心智很接近:事件写在元素旁边,逻辑也跟着组件走。

所以如果你是从 React 过来的,rsx!最舒服的地方,不是它和 JSX 一模一样,而是你不用重新适应一套很陌生的 UI DSL。

3. 它和 JSX 最关键的差异:花括号里是 Rust,不是 JavaScript

这句话我建议直接记住:

rsx!像 JSX,但花括号里跑的是 Rust 表达式。

这会直接影响你写条件、写列表、写事件、写字符串拼接的方式。

3.1 字符串插值走 Rust 的格式化规则

letworld="earth";rsx!{h1{"Hello {world}!"}}

这个感觉更接近 Rust 的format!,不是 JavaScript 模板字符串那套。

3.2 复杂表达式可以直接塞进去

举个例子,如果你想把字符串大写后再渲染:

rsx!{span{{format!("当前用户: {}",current_user_name()).to_uppercase()}}}

只要这个表达式最后能变成 Dioxus 能渲染的东西,就能塞进来。

3.3matchif/else都是正经 Rust 写法

这个地方往往是 React 开发者第一次明显觉得“哦,这里已经不是 JSX 了”。

在 React 里,大家太习惯三元表达式了:

const screen = authenticated ? <Dashboard /> : <Login />;

到了 Dioxus,这里就老老实实按 Rust 来写:

letscreen=ifauthenticated(){rsx!{Dashboard{}}}else{rsx!{Login{}}};rsx!{main{{screen}}}

如果分支再多一点,match往往比 JSX 还顺手:

letbadge=matchstatus.as_str(){"success"=>rsx!{span{class:"ok","已完成"}},"pending"=>rsx!{span{class:"pending","进行中"}},_=>rsx!{span{class:"draft","草稿"}},};rsx!{div{{badge}}}

说白了,Dioxus 没有打算把 Rust 伪装成 JavaScript。它只是给 Rust UI 写法套了一层更像前端的外观。

4. 条件渲染怎么写:别找三元,也别硬套&&

这一段是rsx!里最值得单独适应的地方。

官方文档给了两种思路。

4.1 先算出一个Element,再插进去

letpanel=iflogged_in(){rsx!{UserPanel{}}}else{rsx!{GuestPanel{}}};rsx!{div{{panel}}}

这个写法很稳,逻辑稍微复杂一点时尤其好用。

4.2 直接在rsx!里写内联if

Dioxus 也支持直接在rsx!里写内联if

rsx!{div{iflogged_in(){"你已经登录了"}else{"你还没有登录"}}}

再举个更接近日常页面的例子:

rsx!{section{h2{"发布设置"}ifis_saving(){p{class:"tips","正在保存..."}}ifsave_error().is_some(){p{class:"error","保存失败,请稍后重试"}}}}

这里有两个细节可以顺手记一下:

  • if的分支体是 RSX,不是普通 Rust 语句块
  • 即使没有else,Dioxus 也能正常渲染,缺省分支会变成一个占位节点

所以别再下意识往 React 那套上靠:

{loading && <Spinner />}

在 Dioxus 里,直接写if loading() { Spinner {} }通常更干脆。

5. 列表渲染怎么写:map能用,内联for也能用

如果说条件渲染最容易让 React 用户卡一下,那列表渲染反而是最容易上手的部分。

因为 Dioxus 两种都支持。

5.1 直接用迭代器

lettodos=vec!["读文档","改按钮","接路由"];rsx!{ul{{todos.iter().map(|todo|rsx!{li{"{todo}"}})}}}

这很像 JSX 里的array.map(...)

5.2 用 Dioxus 提供的内联for

官方文档还给了一个更顺手的写法:

lettodos=vec!["读文档","改按钮","接路由"];rsx!{ul{fortodointodos.iter(){li{"{todo}"}}}}

这段我个人挺喜欢。原因很简单:它比{items.iter().map(...)}更像在读结构,不像在读一串链式调用。

5.3 循环里要临时算东西怎么办

也可以,直接包一层表达式:

rsx!{ul{foruserinusers.iter(){{letlabel=format!("{} ({})",user.name,user.role);rsx!{li{"{label}"}}}}}}

这一点也很能体现rsx!的脾气:你不是在写模板循环,你是在 RSX 里继续写 Rust。

6. 属性绑定怎么写:语义像 HTML,写法像 Rust

属性这一块,Dioxus 的规则其实很统一:

属性名后面跟冒号,值写 Rust 表达式。

最基础的是这种:

rsx!{input{class:"search-input",id:"keyword",placeholder:"搜索文章"}}

6.1 动态值直接写表达式

#[component]fnSearchBox()->Element{letmutkeyword=use_signal(String::new);rsx!{input{value:"{keyword}",placeholder:"输入关键字",oninput:move|evt|keyword.set(evt.value())}}}

这段代码里最容易让人停一下的,通常是这一句:

oninput:move|evt|keyword.set(evt.value())

因为很多人脑子里会先冒出event.target.value

但别忘了,你现在已经不在 JS 世界里了。事件参数是 Dioxus 自己的 Rust 类型,取值方式自然也和浏览器原生事件对象不一样。

6.2 布尔属性和条件属性也可以直接算

button{disabled:is_saving(),onclick:move|_|save(),"保存"}

只要最终能算出属性需要的值,就可以直接写进去。

6.3class可以写多次,条件拼样式会更自然

这一点挺实用。

button{class:"btn",class:ifis_active(){"btn-active"},class:ifis_large(){"btn-large"},"切换状态"}

比起手动拼一长串 class 字符串,这种写法干净很多,尤其是状态一多的时候。

7. 常见 HTML 元素和属性,在 Dioxus 里怎么落地

如果你现在脑子里想的是“那我以前那段 HTML 到底该怎么抄过来”,可以先看这张最常用的对照表。

场景HTML / JSX 习惯Dioxus 写法
classclass="card"/className="card"class: "card"
idid="hero"id: "hero"
文本插值{name}"你好,{name}"{name}
事件onClick={...}`onclick: move
输入框取值event.target.valueevt.value()
条件渲染cond ? A : Bif cond { rsx!{ A {} } } else { rsx!{ B {} } }
列表渲染items.map(...){items.iter().map(...)}for item in items.iter()
内联样式style="color:red"style: "color: red;"

这张表当然覆盖不了全部,但把大部分静态 HTML 和基础交互页面迁过来,已经够用了。

8. 样式怎么写:内联、CSS 文件、Tailwind 都能接

这一块是 Dioxus 很讨喜的地方。

它没有自己再发明一套样式系统,而是老老实实站在 HTML + CSS 这边。

8.1 最直接的是内联style

rsx!{div{style:"background-color: #1d4ed8; color: white; padding: 16px; border-radius: 12px;","这是一个带内联样式的卡片"}}

8.2 也可以直接写单个 CSS 属性

这点很多人第一次看到时会有点惊喜:

rsx!{div{background_color:"#1d4ed8",color:"white",padding:"16px",border_radius:"12px","这也是合法写法"}}

也就是说,CSS 属性名可以改成snake_case,直接写进 RSX。

8.3 真正做项目,还是建议样式表分出去

官方文档在 0.7 里推荐用asset!()document::Stylesheet引入样式文件:

usedioxus::prelude::*;staticMAIN_CSS:Asset=asset!("/assets/main.css");#[component]fnApp()->Element{rsx!{document::Stylesheet{href:MAIN_CSS}div{class:"page",h1{"Hello Dioxus"}}}}

对应的assets/main.css就是你熟悉的 CSS:

.page{width:min(720px,calc(100vw - 32px));margin:48px auto;padding:32px;border-radius:20px;background:white;}

8.4 Tailwind 也能接,而且官方就是这么支持的

如果你已经是 Tailwind 用户,Dioxus 这边基本没有额外心智成本。

类名照写:

rsx!{div{class:"flex flex-col gap-4 rounded-2xl bg-white p-6 shadow-lg",h1{class:"text-2xl font-bold","Rust + Dioxus"}p{class:"text-slate-600","这一段就是标准的 Tailwind class。"}}}

按 Dioxus 0.7 官方文档的做法,你在项目根目录放一个tailwind.css

@import"tailwindcss";@source"./src/**/*.{rs,html,css}";

然后在应用里引入生成后的样式:

rsx!{document::Stylesheet{href:asset!("/assets/tailwind.css")}}

这条路为什么舒服?因为你没有被迫去学什么“Rust 专属样式系统”。你原来会的 CSS、Tailwind、选择器、布局思路,大部分都还能接着用。

9. 从 HTML / React 迁过来时,最容易卡住的 4 个点

9.1 不要把rsx!当成模板语言

它长得像模板,但你应该把它看成“Rust 里的声明式 UI 宏”。

一旦这么看,很多写法就顺了。比如循环不是“模板循环标签”,而是迭代器或者for;条件也不是什么“模板指令”,就是ifmatch

9.2 少找event.target.value

输入事件最容易暴露思维惯性。

看到下面这句别慌:

oninput:move|evt|name.set(evt.value())

它不是奇怪,就是更 Rust 一点。

9.3 复杂逻辑先在外面算,再往rsx!里塞

很多人 JSX 写久了,习惯把一大坨条件和拼接都塞进标记里。到 Dioxus 这边,我反而建议你更 Rust 一点:

letheader=ifis_editing(){"编辑文章"}else{"发布文章"};letsubmit_text=ifis_saving(){"保存中..."}else{"保存"};

然后再渲染:

rsx!{h1{"{header}"}button{"{submit_text}"}}

这样读起来通常会更轻松。

9.4 有现成 HTML 时,可以用dx translate

官方文档还提到一个挺实用的工具:dx translate

如果你手里已经有一段 HTML,可以先自动翻成 RSX,再手动整理。迁移现有页面,或者先拿设计稿生成的 HTML 搭个架子,这个命令都能省你不少时间。

总结

rsx!最容易让人误解的地方,是它看起来像“Rust 版 JSX”,但真正决定手感的,其实是后半句:它本质上还是 Rust。

如果把这篇压成几句人话,大概就是:

  1. rsx!不是模板,它是 Rust 宏。
  2. 标签、属性、事件这些地方很像 JSX,所以 React 用户上手会快。
  3. 条件、循环、表达式这些地方遵循 Rust 思维,而不是 JavaScript 思维。
  4. 样式层面直接站在 HTML + CSS + Tailwind 这边,不需要重新学一套新规则。

下一篇我会接着写 Dioxus 的响应式状态管理:use_signaluse_memouse_effect到底怎么配合,为什么它和 React 的useState不是一回事。

如果你已经在写 Dioxus,最开始让你别扭的是条件渲染、事件绑定,还是列表写法?评论区聊聊。

http://www.jsqmd.com/news/1044203/

相关文章:

  • 团队博文06项目总结
  • 海南省高口碑黄金铂金回收白银回收实体老店排行 5 家靠谱门店电话地址全收录
  • 2026厦门黄金回收去哪好|本地正规门店榜单,收的顶实力推荐 - 奢侈品回收评测
  • 一文读懂BiDi单纤双向光模块:一芯双传,高效盘活光纤资源
  • 实验室“隐形冠军”的采购哲学:不做加法做减法
  • 张家口车灯升级维修哪家专业?幻影车灯深耕行业15年解决各类大灯疑难问题 - 速递信息
  • 深入解析MMC2001 OnCE调试模块:追踪计数器与调试模式实战指南
  • 2026昆山卫生间防水服务场景适配与合规服务商深度解析——以昆山鼎壹万防水补漏公司为核心参考 专业防水公司排名推荐(2026年6月防水补漏最新TOP权威排名) - 鼎壹万修缮说
  • GEO优化可以批量覆盖行业关键词吗
  • Syncthing开源同步工具:从零到一,构建你的首个跨设备文件同步网络
  • 华硕笔记本终极控制指南:如何用G-Helper轻松替代臃肿的Armoury Crate
  • 如何3分钟搞定百度网盘批量转存?这个免费工具让你效率翻倍!
  • 2026成都黄金出手全攻略:行情周期判断、验金要点、结算避坑全解析 - 奢侈品回收评测
  • 从告警到根因只需几秒:基于 AI 驱动的可观测性,使用 Elastic Agent Builder 和 Workflows
  • 团队博文01注册团队博客地址
  • Windows热键冲突检测神器:Hotkey Detective深度技术解析 [特殊字符]️‍♂️
  • 2026年6月优秀的去内毛刺焊管/汽车用焊管厂家推荐恒丰祥钢管,窄公差尺寸统一提升零部件装配契合度 - 品牌鉴赏师
  • 苏州家长速看!2026 年专业戒网瘾学校 TOP10,叛逆、厌学、沉迷手机全解决! - 辛云教育资讯
  • 邵阳家长速藏!2026 年十大叛逆厌学戒网瘾学校权威榜单,帮孩子重回阳光青春! - 辛云教育资讯
  • 多卡并行推理实战,vLLM 张量并行配置与性能测试
  • 2026成都旧金首饰变现实操攻略,拆解磨损扣费、旧料折旧行业规则 - 奢侈品回收评测
  • Tomcat CVE-2017-12615漏洞原理与实战复现:从任意文件上传到RCE
  • 2026年6月优秀的古建瓦厂家推荐富美建筑陶瓷,适配套房景区商业街打造统一国风仿古屋面景观 - 品牌鉴赏师
  • 智能黑苹果配置革命:OpCore Simplify如何用AI思维重塑OpenCore体验
  • QuickLook Office预览插件完整指南:3秒快速查看Word、Excel、PPT文件
  • Windows下CMake交叉编译:破解“无法编译简单测试程序”的困局
  • AMD 显卡跑大模型,ROCm 7.x 加 vLLM 部署避坑指南
  • 转行学充电桩维修培训 高口碑正规培训机构选这家 - 湖南阳光技术
  • 2026寄快递怎么省钱?新手避坑必看攻略 - 快递物流资讯
  • 终极解决方案:如何一键修复Kindle电子书封面,让数字书架重焕光彩