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 包 中使用

MIT/Apache

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());
}

许可证

此项目根据您选择之一获得许可

供您选择。

贡献

除非您明确声明,否则您有意提交以包含在 view 中的任何贡献,根据 Apache-2.0 许可证的定义,将按上述方式双重许可,而不附加任何额外的条款或条件。

无运行时依赖