03-StyleStringBuilder:类型安全的样式构建系统

目录

概述

StyleStringBuilder 是 Hikari 的内联样式构建器,提供了类型安全的 CSS 属性设置方式。它通过 CssProperty 枚举和便捷方法,完全替换了传统的 style 字符串拼接,实现了编译时属性名检查和运行时零开销。

更新 (Phase 2):StyleStringBuilder 和 CssProperty 现在从 tairitsu-style re-export,提供 403 个 W3C 标准 CSS 属性

设计理念

核心原则

  1. 1
    类型安全 - 编译时检查 CSS 属性名
  2. 2
    像素值优化 - 自动 px 单位转换
  3. 3
    紧凑输出 - 去除冗余空格
  4. 4
    CSS 变量支持 - 完美集成主题系统

与 ClassesBuilder 的区别

特性ClassesBuilderStyleStringBuilder
输出class 属性style 属性
使用场景静态布局工具类动态计算值、覆盖全局样式
类型安全工具类枚举CSS 属性枚举
运行时开销零(编译时)零(字符串连接)
示例hi-p-4padding:16px

架构层次

mermaid

核心架构

1. StyleStringBuilder 结构

定义位置packages/animation/src/style.rs

rust
1
2
3
pub struct StyleStringBuilder {
    styles: Vec<(CssProperty, String)>,
}

核心方法

方法职责返回值
new()创建 builderStyleStringBuilder
add(property, value)添加 CSS 属性StyleStringBuilder
add_px(property, pixels)添加像素值(自动加 px)StyleStringBuilder
build()构建样式字符串(带空格)String
build_clean()构建紧凑样式字符串(无空格)String

2. CssProperty 枚举

定义位置packages/animation/src/style.rs

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
pub enum CssProperty {
    // Layout
    Display,
    Position,
    Top,
    Right,
    Bottom,
    Left,
    ZIndex,

    // Box Model
    Width,
    MinWidth,
    MaxWidth,
    Height,
    MinHeight,
    MaxHeight,
    Padding,
    Margin,
    Border,
    BorderRadius,

    // Flexbox
    Flex,
    FlexDirection,
    AlignItems,
    JustifyContent,
    Gap,

    // Typography
    FontFamily,
    FontSize,
    FontWeight,
    LineHeight,
    Color,

    // Visual
    Opacity,
    Background,
    BackgroundColor,
    BoxShadow,
    Transform,
    TransformOrigin,

    // ... 更多属性
}

属性映射

rust
1
2
3
4
5
6
7
8
9
10
impl CssProperty {
    pub fn as_str(&self) -> &'static str {
        match self {
            CssProperty::Display => "display",
            CssProperty::Opacity => "opacity",
            CssProperty::Transform => "transform",
            // ... 自动转换为 kebab-case
        }
    }
}

3. 像素值自动转换

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
impl StyleStringBuilder {
    /// 添加像素值(自动添加 px 单位)
    ///
    /// # Example
    ///
    /// ```rust
    /// let style = StyleStringBuilder::new()
    ///     .add_px(CssProperty::Width, 100)  // => "width:100px"
    ///     .add_px(CssProperty::Height, 200) // => "height:200px"
    ///     .build_clean();
    /// ```
    pub fn add_px(mut self, property: CssProperty, pixels: i32) -> Self {
        let value = format!("{}px", pixels);
        self.styles.push((property, value));
        self
    }
}

工作机制

构建流程

mermaid

紧凑输出机制

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
impl StyleStringBuilder {
    /// 构建紧凑样式字符串(无空格)
    ///
    /// 输出: "property:value;property:value"
    pub fn build_clean(self) -> String {
        self.styles
            .into_iter()
            .map(|(property, value)| {
                format!("{}:{}", property.as_str(), value)
            })
            .collect::<Vec<_>>()
            .join(";")
    }

    /// 构建标准样式字符串(带空格)
    ///
    /// 输出: "property: value; property: value"
    pub fn build(self) -> String {
        self.styles
            .into_iter()
            .map(|(property, value)| {
                format!("{}: {}", property.as_str(), value)
            })
            .collect::<Vec<_>>()
            .join("; ")
    }
}

类型检查机制

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// no 编译错误:属性名拼写错误
let style = StyleStringBuilder::new()
    .add(CssProperty::Widht, "100px")  // 没有这个变体
    .build();

// no 编译错误:参数类型错误
let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, "100px")  // 应该是 i32
    .build();

// yes 编译通过:IDE 自动补全
let style = StyleStringBuilder::new()
    .add(CssProperty::Width, "100px")  // IDE 提示 Width 变体
    .build();

CSS 属性枚举

完整属性列表

布局属性

枚举变体CSS 属性示例值
Displaydisplayflex, block, none
Positionpositionrelative, absolute, fixed
Toptop10px, 50%
Rightright10px, 50%
Bottombottom10px, 50%
Leftleft10px, 50%
ZIndexz-index10, 100

盒模型属性

枚举变体CSS 属性示例值
Widthwidth100px, 50%, auto
Heightheight100px, 50%, auto
MinWidthmin-width100px
MaxWidthmax-width1000px
Paddingpadding16px, 1rem
Marginmargin16px, 1rem
BorderRadiusborder-radius8px, 50%

弹性布局属性

枚举变体CSS 属性示例值
FlexDirectionflex-directionrow, column
AlignItemsalign-itemscenter, flex-start
JustifyContentjustify-contentcenter, space-between
Gapgap16px, 1rem
FlexGrowflex-grow1, 0

视觉属性

枚举变体CSS 属性示例值
Opacityopacity0.5, 1
Transformtransformscale(1.1), translate(10px)
TransformOrigintransform-origincenter, top
BoxShadowbox-shadow0 2px 4px rgba(0,0,0,0.1)
Backgroundbackgroundred, url(...)

字体属性

枚举变体CSS 属性示例值
FontSizefont-size16px, 1rem
FontWeightfont-weight400, bold
LineHeightline-height1.5, 2
Colorcolorred, #ff0000

性能优化

1. 零运行时开销

编译时确定:所有属性名在编译时确定

rust
1
2
3
4
// 编译后等同于:
let style = "width:100px;height:200px;opacity:0.5";

// 不需要运行时拼接属性名

2. 紧凑输出

rust
1
2
3
4
5
6
7
8
9
10
11
// yes 推荐:紧凑输出(减少字节)
let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, 100)
    .build_clean();
// 输出: "width:100px"

// no 避免:标准输出(带空格)
let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, 100)
    .build();
// 输出: "width: 100px"

3. 避免 clone

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
impl StyleStringBuilder {
    pub fn add(mut self, property: CssProperty, value: impl Into<String>) -> Self {
        // Into<String> 避免不必要的 clone
        let value = value.into();
        self.styles.push((property, value));
        self
    }
}

// yes 推荐:使用 &str(零成本转换)
let style = StyleStringBuilder::new()
    .add(CssProperty::Width, "100px")
    .build();

// yes 也支持:使用 String(会移动所有权)
let width = "100px".to_string();
let style = StyleStringBuilder::new()
    .add(CssProperty::Width, width)
    .build();

使用示例

示例 1:基础样式

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use hikari_animation::style::{StyleStringBuilder, CssProperty};

let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, 100)
    .add_px(CssProperty::Height, 50)
    .add(CssProperty::BackgroundColor, "red")
    .build_clean();

// 输出: "width:100px;height:50px;background-color:red"

rsx! {
    div { style: "{style}",
        "内容"
    }
}

示例 2:CSS 变量

rust
1
2
3
4
5
6
7
let style = StyleStringBuilder::new()
    .add(CssProperty::Opacity, "var(--hi-dropdown-opacity)")
    .add(CssProperty::Transform, "scale(var(--hi-dropdown-scale))")
    .add(CssProperty::TransformOrigin, "top center")
    .build_clean();

// 输出: "opacity:var(--hi-dropdown-opacity);transform:scale(var(--hi-dropdown-scale));transform-origin:top center"

示例 3:覆盖全局样式

rust
1
2
3
4
5
6
7
8
9
10
11
// 覆盖 img { height: auto; } 全局样式
let img_style = StyleStringBuilder::new()
    .add_px(CssProperty::Height, 36)
    .add_px(CssProperty::MaxWidth, 140)
    .add(CssProperty::Width, "auto")
    .add(CssProperty::ObjectFit, "contain")
    .build_clean();

rsx! {
    img { style: "{img_style}", src: "..." }
}

示例 4:动态计算值

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let width = use_memo(move || {
    let window_width = window().inner_width().unwrap();
    (window_width.as_f64().unwrap() * 0.8) as i32
});

let style = use_memo(move || {
    StyleStringBuilder::new()
        .add_px(CssProperty::Width, *width.read())
        .build()
});

// 动态计算宽度
rsx! {
    div { style: "{style}",
        "自适应宽度"
    }
}

示例 5:组合使用 ClassesBuilder 和 StyleStringBuilder

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use hikari_palette::classes::{ClassesBuilder, Display, FlexDirection, Gap};
use hikari_animation::style::{StyleStringBuilder, CssProperty};

// ClassesBuilder 处理布局
let classes = ClassesBuilder::new()
    .add(Display::Flex)
    .add(FlexDirection::Row)
    .add(Gap::Gap4)
    .build();

// StyleStringBuilder 处理动态值
let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, computed_width)
    .add(CssProperty::Opacity, "0.8")
    .build_clean();

rsx! {
    div { class: "{classes}", style: "{style}",
        "内容"
    }
}

总结

StyleStringBuilder 通过类型安全的样式构建系统,实现了:

  1. 1
    编译时安全 - 防止 CSS 属性名拼写错误
  2. 2
    像素值优化 - 自动 px 单位转换
  3. 3
    紧凑输出 - 减少字节传输
  4. 4
    CSS 变量支持 - 完美集成主题系统
  5. 5
    零运行时开销 - 纯字符串连接

这套系统完全替换了传统的 style 字符串拼接,是 Hikari 动态样式体系的核心组件。

Phase 2 迁移

迁移概述

在 Hikari 到 Tairitsu 构建链迁移的 Phase 2 中,StyleStringBuilder 和 CssProperty 已从内部实现迁移到共享的 tairitsu-style 库。

迁移前后对比

Before (Phase 2 前):

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// packages/animation/src/properties.rs
pub enum CssProperty {
    // Layout
    Display,
    Position,
    Top,
    Right,
    Bottom,
    Left,
    ZIndex,

    // Box Model
    Width,
    MinWidth,
    MaxWidth,
    Height,
    MinHeight,
    MaxHeight,
    Padding,
    Margin,
    Border,
    BorderRadius,

    // ... ~50 properties manually defined
}

After (Phase 2 后):

rust
1
2
3
4
5
// packages/animation/src/style/mod.rs
// Re-export from tairitsu_style
pub use tairitsu_style::{StyleStringBuilder, CssProperty, Property};

// Now provides 403 W3C standard properties

属性数量对比

指标BeforeAfter提升
CSS 属性数量~50403+706%
手动维护代码行数6350-100%
W3C 标准覆盖率~12%100%+733%

完整的 403 个 CSS 属性

迁移后,CssProperty 枚举现在包含以下完整类别的属性:

布局属性 (Layout)

盒模型属性 (Box Model)

弹性布局属性 (Flexbox)

网格布局属性 (Grid)

排版属性 (Typography)

颜色与背景属性 (Color & Background)

视觉效果属性 (Visual Effects)

过渡与动画属性 (Transition & Animation)

列表属性 (Lists)

表格属性 (Tables)

用户界面属性 (User Interface)

多列布局属性 (Multi-column)

其他属性 (Miscellaneous)

使用变化

迁移后,使用方式保持不变(通过 re-export):

rust
1
2
3
4
5
6
7
// Before and After (same usage)
use hikari_animation::style::{StyleStringBuilder, CssProperty};

let style = StyleStringBuilder::new()
    .add_px(CssProperty::Width, 100)
    .add(CssProperty::BackgroundColor, "red")
    .build_clean();

但你现在可以使用更多的 CSS 属性:

rust
1
2
3
4
5
6
7
8
// 新增的属性示例
let style = StyleStringBuilder::new()
    .add(CssProperty::GridTemplateColumns, "repeat(3, 1fr)")
    .add(CssProperty::Gap, "1rem")
    .add(CssProperty::BackdropFilter, "blur(10px)")
    .add(CssProperty::Filter, "drop-shadow(0 4px 6px rgba(0,0,0,0.1))")
    .add(CssProperty::MixBlendMode, "multiply")
    .build_clean();

代码清理

迁移删除了以下文件:

text
1
packages/animation/src/properties.rs  (635 lines)

并简化了 packages/animation/src/style/mod.rs

rust
1
2
3
4
5
6
7
8
9
10
11
12
// Before
mod properties;
pub use properties::CssProperty;

pub struct StyleStringBuilder {
    styles: Vec<(CssProperty, String)>,
}

// ... manual property mapping

// After
pub use tairitsu_style::{StyleStringBuilder, CssProperty, Property};

兼容性

所有现有代码继续工作,无需修改:

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
// 所有现有的用法都继续工作
use hikari_animation::style::CssProperty;

// yes 仍然有效
CssProperty::Width
CssProperty::Height
CssProperty::BackgroundColor

// yes 新增属性也可用
CssProperty::GridTemplateColumns
CssProperty::BackdropFilter
CssProperty::Filter
CssProperty::MixBlendMode

性能影响

迁移后性能提升:

测试覆盖

所有现有测试继续通过:

bash
1
2
3
cargo test -p hikari-animation --lib

test result: ok. 24 passed; 0 failed; 0 ignored

升级指南

如果要在新组件中使用新增的 CSS 属性,只需正常导入和使用:

rust
1
2
3
4
5
6
7
8
9
use hikari_animation::style::{StyleStringBuilder, CssProperty};

// 使用任何 403 个 CSS 属性
let style = StyleStringBuilder::new()
    .add(CssProperty::Display, "grid")
    .add(CssProperty::GridTemplateColumns, "repeat(auto-fit, minmax(200px, 1fr))")
    .add(CssProperty::GridAutoRows, "minmax(100px, auto)")
    .add(CssProperty::Gap, "1rem")
    .build_clean();

更多信息