classnames-rs 库
大家好, 好久没有更新文章了, 接下来我会分享一些 rust, Dioxus 相关的知识. 并会翻译 Dioxus 的官方文档. 有兴趣的可以关注一下哦.
介绍
虽然我已经接触 rust
好多年了, 但也是最近才使用它来开发后台服务, 并且最近开始学习 Dioxus
框架, 他是一个 rust 开发的跨平台 GUI 库.他利用 web 技术, 能够做到一份代码在多平台运行.和 tauri 不同的是, Dioxus 有自己的 DSL
他和 rust 代码有很好的结合, 并且利用 IDE 能做到很好的代码提示.
在接下来的学习过程中, 我会翻译其官方文档并发布在该公众号. 喜欢 Dioxus 的朋友可以关注一下我, 我们一起学习进步.我也会尽可能在学习过程中结合自己的使用, 分析一些更有趣的 demo.
来自 classnames 的灵感
今天先来分享一下我写的 classnames-rs, classnames-const-rs 库, 它的灵感来自前端的 classnames
库.我在使用 Dioxus 进行web 开发时, 发现处理和管理 class 样式代码有很多缺点
- 样式混杂不清晰, 无法注解,也不利于后期维护
- 对于一些条件可选的样式, 会导致代码充斥了大量的样板代码
缺点分析
可选的组件样式
就如下面代码中的条件判断, 每次组件收到条件都要进行必要的判断:
rust
let icon_class = if let Some(class) = &props.class {
format!("animate-spin {}", class) // 对于 loading 状态添加旋转动画
} else if matches!(props.status, ValidationStatus::Loading) {
"animate-spin".to_string()
} else {
String::new()
};
我们需要判断通过组件传入的 class 样式是否为 None, 如果不为 None, 则进行拼接.
动态的条件判断
再比如下面的代码, 我们需要根据动态的状态 (比如是否 disabled), 来控制其样式
rust
let class = if props.is_support {
props.product.class()
} else {
"text-zinc-400 dark:text-zinc-500 opacity-60 grayscale".to_string()
};
混杂的样式定义
当我们定义样式时, 很容易定义成如下的样式
rust
pub const BASE_STYLE: &str = "relative block w-full before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm";
使用 classnames-rs 库
正是因为这些缺点,我开发了这2个库
classnames-rs
运行时处理样式classnames-const-rs
编译时处理样式,确保0运行时开销
classnames-const-rs 库
我们可以使用 classnames-const-rs
来优化静态样式的定义, 如下:
rust
pub(crate) const CONTROL: &str = classnames_concat!(
// Basic layout
"relative block w-full",
// Background color + shadow applied to inset pseudo element, so shadow blends with border in light mode
"before:absolute before:inset-px before:rounded-[calc(var(--radius-lg)-1px)] before:bg-white before:shadow-sm",
// Background color is moved to control and shadow is removed in dark mode so hide `before` pseudo
"dark:before:hidden",
// Focus ring
"after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-transparent after:ring-inset sm:focus-within:after:ring-2 sm:focus-within:after:ring-blue-500",
// Disabled state
"has-data-disabled:opacity-50 has-data-disabled:before:bg-zinc-950/5 has-data-disabled:before:shadow-none",
// Invalid state
"has-data-invalid:before:shadow-red-500/10",
);
我们可以自由的注释样式含义, 方便管理和后期维护.而无需担心运行时的开销, 因为都是静态字符串, 会在编译时进行字符串拼接处理.
classnames-rs 库
我们可以使用该库来管理所有动态的条件判断:
rust
let class = classnames!(
CONTROL,
choose!(props.is_support, props.product.class(), "text-zinc-400 dark:text-zinc-500 opacity-60 grayscale"),
maybe!(props.class),
when!(is_activate, "btn btn-active")
);
几行代码就实现了之前需要多个 if else
的样板代码才能实现的功能. 我来介绍一下库中的主要宏
classnames!
主要宏, 用来拼接多个class
样式表达式choose!
如代码的if else
其参数顺序为 判断表达式,true_value
表达式,false_vlaue
表达式maybe!
用来处理Option<T>
类型when!
是 单条件的写法 结合使用这些宏, 可以极大的方便 class 样式的编写.
总结
我们使用 classnames-const-rs
库的 classnames_concat!
宏来定义管理静态的基础样式, 他会在编译时生成静态字符串对象, 没有运行时开销. 使用 classnames-rs
的 classnames!
宏来组装运行时的条件判断.
感谢看到最后, 这是我写的第一篇公众号文章, 文章可能有不少啰嗦不当之处, 敬请谅解.