macros-overview[20261004210505]

0 阅读1分钟

euv 宏系统概览

项目代码:github.com/euv-dev/euv

euv 提供了一套丰富的宏系统,构成了开发者体验的支柱。这些宏涵盖了从组件定义、HTML 模板组合到响应式状态管理和 CSS 注入的方方面面。了解每个宏及其协同工作方式,对于高效开发 euv 应用至关重要。本文将全面介绍 euv 中所有可用的宏:computed!、watch!、class!、html!、css-vars!、var! 和 component。

html! 宏 —— HTML 模板

html! 宏是 euv 中使用最频繁的宏。它允许你直接在 Rust 代码中编写类似 HTML 的语法,在编译时将其转换为虚拟 DOM 节点。这种方法既保留了 HTML 模板的熟悉感,又具备 Rust 类型系统的安全性。

以下是 html! 宏的一个简单示例:

#[component]
pub fn my_card(node: VirtualNode<MyCardProps>) -> VirtualNode {
    let MyCardProps { title, .. } = node.try_get_props().unwrap_or_default();
    let children: VirtualNode = node.try_get_child_node();
    html! {
        div {
            h3 { title }
            children
        }
    }
}

html! 宏支持:

  • 嵌套元素:元素可以通过花括号相互嵌套,镜像 HTML 的结构。
  • 文本内容:纯文本可以直接放在元素块内。
  • 子节点转发:通过 try_get_child_node() 获取的 children 变量可以放置在模板的任何位置。
  • 属性:元素属性通过属性名后跟冒号和值来指定。

html! 宏是 euv 中所有 UI 组合的基础。每个组件的渲染输出都使用此宏构建。

component 宏 —— 组件定义

#[component] 属性宏将函数标记为 euv 组件。该宏使函数能够参与虚拟 DOM 渲染管线,让组件能够访问 props、子节点和响应式系统。

#[derive(Clone, Default)]
struct MyCardProps {
    title: &'static str,
}

#[component]
pub fn my_card(node: VirtualNode<MyCardProps>) -> VirtualNode {
    let MyCardProps { title, .. } = node.try_get_props().unwrap_or_default();
    let children: VirtualNode = node.try_get_child_node();
    html! {
        div {
            h3 { title }
            children
        }
    }
}

使用 #[component] 标记的函数必须:

  1. 接受一个 VirtualNode 作为参数(该节点携带 props 和子内容)。
  2. 返回一个 VirtualNode,表示组件的渲染输出。

#[component] 宏还与 html! 宏协同工作,确保组件的模板正确集成到虚拟 DOM 树中。

var! 宏 —— 响应式变量

var! 宏用于在组件内创建响应式变量(信号)。当响应式变量发生变化时,任何依赖它的 UI 部分都会自动重新渲染。

let shared_text: Signal<String> = use_signal(|| "Type here...".to_string());

虽然底层 API 是 use_signal,但 var! 宏提供了一种更具人体工程学的方式来声明响应式状态。使用 var! 创建的变量是响应式的——在 html! 模板中读取它们会建立订阅,向它们写入会触发所有依赖 UI 的重新渲染。

响应式变量是 euv 中状态管理的基本构建块。它们保存随时间变化的值,框架确保 UI 始终反映当前状态。

computed! 宏 —— 派生信号

computed! 宏创建派生信号——从其他信号自动计算的响应式值。当任何源信号发生变化时,派生信号会重新计算。

派生信号对于保持组件逻辑清晰至关重要。你无需手动在多个变量之间同步状态,而是可以声明派生值,让 euv 处理同步工作。

例如,如果你有 celsius 和 fahrenheit 信号,可以使用 computed! 创建一个始终反映当前温度单位的派生信号。结合 watch!,计算信号构成了一个强大的响应式图。

watch! 宏 —— 信号联动

watch! 宏观察一个或多个信号,并在其中任何一个发生变化时执行回调。这是 euv 中副作用和跨信号协调的主要机制。

观察单个信号

watch!(celsius, |celsius_value: f64| {
    fahrenheit.set(celsius_value * 9.0 / 5.0 + 32.0);
});

当 celsius 信号变化时,回调接收新值并相应地更新 fahrenheit 信号。

观察多个信号

watch!(red, green, blue, |r, g, b| {
    hex_color.set(format!("#{:02x}{:02x}{:02x}", r, g, b));
});

当 red、green 或 blue 中任何一个发生变化时,回调接收所有三个当前值并计算十六进制颜色字符串。

watch! 宏是声明式的——你描述当信号变化时应该发生什么,euv 处理调度和执行。这与命令式事件处理形成对比,在命令式事件处理中,你需要手动绑定事件监听器并更新状态。

class! 宏 —— CSS 类

class! 宏提供了一种在 euv 组件中定义和管理 CSS 类的方式。它允许你指定应用于模板元素的样式。

euv 还支持内联样式和 CSS 变量(通过 css-vars!),但 class! 宏是将预定义样式应用到组件的主要方式。它与框架的 CSS 动画系统集成了过渡和关键帧动画的支持。

css-vars! 宏 —— CSS 变量

css-vars! 宏允许你向组件中注入 CSS 自定义属性(CSS 变量)。CSS 变量非常强大,因为它们可以动态更改并由子元素继承。

通过 css-vars!,你可以定义随信号变化而更新的响应式 CSS 变量,实现动态主题和响应式样式,而无需编写 JavaScript。

Css::inject_css() 函数 —— 自定义关键帧

对于高级动画需求,euv 提供了 Css::inject_css() 函数,允许你在运行时将自定义 CSS 关键帧动画注入页面。这与内置的关键帧动画(如 euv-spin、euv-fade-in、euv-scale-in、euv-pulse、euv-slide-up、euv-slide-left、euv-fade-in-up、euv-progress 和 euv-shimmer)配合使用。

宏协同工作:完整示例

让我们看一个这些宏如何在实际示例中协同工作:

#[derive(Clone, Default)]
struct MyCardProps {
    title: &'static str,
}

#[component]
pub fn my_card(node: VirtualNode<MyCardProps>) -> VirtualNode {
    let MyCardProps { title, .. } = node.try_get_props().unwrap_or_default();
    let children: VirtualNode = node.try_get_child_node();
    html! {
        div {
            h3 { title }
            children
        }
    }
}

在这个示例中:

  • #[component] 将函数标记为 euv 组件。
  • html! 定义组件的模板结构。
  • Props 使用 try_get_props() 提取,子节点使用 try_get_child_node() 获取。

以下是一个带有信号的更动态的示例:

let shared_text: Signal<String> = use_signal(|| "Type here...".to_string());

pub fn child_input(text_signal: Signal<String>, count_signal: Signal<i32>) -> VirtualNode {
    html! {
        div {
            input {
                r#type: "text"
                value: text_signal.get()
                oninput: on_input_value(text_signal)
            }
        }
    }
}

在这里,use_signal 创建了一个响应式状态变量,html! 定义了从中读取的模板,on_input_value 处理用户输入以更新信号。

总结

宏用途
#[component]将函数标记为 euv 组件
html!将类似 HTML 的模板组合为虚拟 DOM 节点
var!创建响应式变量(信号)
computed!从其他信号创建派生信号
watch!观察信号并在变化时执行回调
class!定义和管理 CSS 类
css-vars!注入 CSS 自定义属性
Css::inject_css()注入自定义 CSS 关键帧动画

每个宏都有其特定用途,它们共同构成了一个用于在 Rust 中构建响应式、类型安全的 Web 应用的统一系统。掌握这些宏,你将能够充分发挥 euv 框架的全部力量。


项目代码:github.com/euv-dev/euv