11 个版本 (4 个破坏性更改)
0.4.1 | 2020 年 7 月 28 日 |
---|---|
0.4.0 | 2020 年 7 月 28 日 |
0.3.0 | 2020 年 7 月 28 日 |
0.2.0 | 2020 年 7 月 27 日 |
0.0.3 | 2019 年 10 月 31 日 |
#720 in GUI
在 2 包 中使用
26KB
355 行
🏗️ View
[dependencies]
view = "0.4"
Rust 中构建视图层次结构有点繁琐。这是一个非框架特定的构建视图的宏。与 JSX 等技术相比,它更注重结构化,并借鉴了一些 SwiftUI 的思想。
此示例展示了所有可能的内容
let images = vec!["coffee.png","cream.png","sugar.png"];
let show_coupon = false;
let v = view!{
VStack {
Image("company.png")
Button(text:"order".to_string(),style:BOLD)
.on_click(|| do_order()) {
Image("order_icon.png")
}
For(i in images.iter()) { Image(i) }
If(show_coupon) { Coupon }
Legal
}
};
以下是此宏为您节省的所有代码。
let images = vec!["coffee.png", "cream.png", "sugar.png"];
let show_legal = false;
let s = {
let mut o = VStack {
..Default::default()
};
o.add_view_child({
let mut o = Image::new("company.png");
o
});
o.add_view_child({
let mut o = Button {
text: "order".to_string(),
style: BOLD,
..Default::default()
};
o.on_click(|| do_order());
o.on_click(|| do_order());
o.add_view_child({
let mut o = Image::new("order_icon.png");
o
});
o
});
for i in images.iter() {
o.add_view_child({
let mut o = Image::new(i);
o
});
}
o.add_view_child({
let mut o = Footer {
..Default::default()
};
o
});
if show_legal {
o.add_view_child({
let mut o = Legal {
..Default::default()
};
o
});
}
o
};
此项目不是针对特定框架的,但它有一些规则
- 有子视图的视图必须实现
add_view_child
函数 - 视图必须实现用于属性构建的 Default 特性(例如
Button(text:""click me""))
) - 视图必须有一个 'new' 构造函数进行简单构建(例如
Button(text""click me"")
)
以下是一个实现这些规则的基本示例,尽管您可以使用您喜欢的任何特性以任何方式实现它们。
trait View {}
#[derive(Default)]
struct VStack {
direction: u8,
children: Vec<Box<View>>
}
impl VStack {
fn new(direction:u8) -> Self {
VStack{ direction:direction, children:vec![] }
}
fn add_view_child<'a, T>(&'a mut self, child: Box<T>)
where
T: 'static + View,
{
self.children.push(child);
}
}
impl View for VStack {}
#[derive(Default)]
struct Button {
text:String
}
impl Button {
fn new(text:String) -> Self {
Button{text:text}
}
}
impl View for Button {}
让我们创建一个虚拟 DOM
use view::*;
struct VNode {
vnode_type: &'static str,
children: Vec<VNode>,
classes: Vec<String>,
text: Option<String>,
}
impl VNode {
fn add_class(&mut self, c: &str) {
self.classes.push(c.to_string())
}
fn add_view_child(&mut self, child: VNode) {
self.children.push(child);
}
fn render_to_string(&self) -> String {
if let Some(t) = &self.text {
t.clone()
} else {
format!(
"<{} class=\"{}\">{}</{}>",
self.vnode_type,
self.classes.join(","),
self.children
.iter()
.map(|c| c.render_to_string())
.collect::<Vec<String>>()
.join(""),
self.vnode_type
)
}
}
}
type Div = VNode;
impl Default for Div {
fn default() -> Self {
VNode {
vnode_type: "div",
children: vec![],
classes: vec![],
text: None,
}
}
}
type Text = VNode;
impl Text {
fn new(t: &str) -> Self {
VNode {
vnode_type: "text",
children: vec![],
classes: vec![],
text: Some(t.to_string()),
}
}
}
fn main() {
let html = view! {
Div.add_class("greeting"){
Text("Hello World!")
}
};
println!("{}", html.render_to_string());
}
许可证
此项目根据您选择之一获得许可
- Apache 许可证第 2 版 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
供您选择。
贡献
除非您明确声明,否则您有意提交以包含在 view 中的任何贡献,根据 Apache-2.0 许可证的定义,将按上述方式双重许可,而不附加任何额外的条款或条件。