#render-template #jsx #html #template #rsx #html-rendering

nightly render

一个安全简单的模板引擎,具有 JSX 的用户体验

5 个不稳定版本

0.3.1 2020年6月1日
0.3.0 2019年11月27日
0.2.0 2019年9月23日
0.1.1 2019年9月21日
0.1.0 2019年9月21日

#142 in 模板引擎

Download history 6/week @ 2023-11-20 2/week @ 2023-11-27 2/week @ 2023-12-04 64/week @ 2023-12-11 214/week @ 2023-12-18 206/week @ 2023-12-25 148/week @ 2024-01-01 21/week @ 2024-01-08 10/week @ 2024-02-12 10/week @ 2024-02-19 22/week @ 2024-02-26 24/week @ 2024-03-04

66 每月下载次数
render_macros 中使用

MIT 许可证

17KB
228

render

🔏 一个安全简单的模板引擎,具有 JSX 的用户体验

render 本身是特性和结构的组合,通过这些特性、结构和宏的组合,可以统一并提升组合树形数据结构的体验。这对于 HTML 和 XML 渲染效果最好,但也适用于其他用途,如 ReasonML 的 Pastel 库用于终端颜色。

如何实现?

一个可渲染的组件是一个实现 Render 特性的结构体。有多个宏提供了更好的实现 Renderable 的体验

  • #[component] 用于使用函数定义组件
  • rsx! 用于使用 JSX 用户体验组合元素
  • html! 用于组合元素并将它们渲染成字符串

这与...有何不同?

handlebars?

Handlebars 是一个很棒的规范,让我们开发者定义模板,并在各种语言和框架之间无缝工作。不幸的是,由于其规范,它不能保证 Rust 的类型安全性,这迫使你必须像在动态类型语言中那样为视图编写测试来验证类型。在像 Rust 这样的类型安全语言中,这些测试是不必要的 - 但 Handlebars 是以 JSON 为导向的,这与 Rust 的类型系统不符。

render 提供了 Rust 提供的相同级别的类型安全性,没有牺牲用户体验或速度。

typed-html?

typed-html 是一个很棒的库。不幸的是,它专注于 HTML 规范的严格性,不允许任意组合自定义元素。

render 采用不同的方法。目前,HTML 完全没有类型。它可以获取任何键和任何字符串值。主要关注自定义组件,因此您可以创建一个可组合的声明性模板,而不会出现运行时错误。

使用方法

注意:目前 render 需要 nightly Rust 编译器,因此它将具有卫生宏。

这意味着您需要在您的根目录下的 lib.rs/main.rs

中添加以下功能标志。

#![feature(proc_macro_hygiene)]

简单的HTML渲染

为了将简单的HTML片段渲染成一个 String,使用 rsx! 宏生成组件树,然后调用它上的 render 方法。

#![feature(proc_macro_hygiene)]

use render::{rsx, Render};

let tree = rsx! {
  <div>
    <h1>{"Hello!"}</h1>
    <p>{"Hello world!"}</p>
  </div>
};

assert_eq!(tree.render(), "<div><h1>Hello!</h1><p>Hello world!</p></div>");

因为这很常见,所以还有一个名为 html! 的宏,该宏调用 rsx! 来生成组件树,然后调用它上的 render 方法。大多数时候,您会发现自己使用 rsx! 宏来组合任意组件,只有在需要字符串输出时(如发送响应或生成Markdown文件)才会调用 html!

在Render中,属性和纯字符串使用 render::html_escaping 模块进行转义。为了使用未转义值以便您可以安全地插入原始HTML,请使用 raw! 宏包围您的字符串。

#![feature(proc_macro_hygiene)]

use render::{html, raw};

let tree = html! {
  <div>
    <p>{"<Hello />"}</p>
    <p>{raw!("<Hello />")}</p>
  </div>
};

assert_eq!(tree, "<div><p>&lt;Hello /&gt;</p><p><Hello /></p></div>");

自定义组件

Render最大的能力是提供类型安全以及自定义可渲染组件。引入新组件就像定义一个返回 Render 值的函数一样简单。

为了从其他组件或HTML节点构建组件,您可以使用 rsx! 宏,该宏生成一个 Render 组件树。

#![feature(proc_macro_hygiene)]

use render::{component, rsx, html};

#[component]
fn Heading<'title>(title: &'title str) {
  rsx! { <h1 class={"title"}>{title}</h1> }
}

let rendered_html = html! {
  <Heading title={"Hello world!"} />
};

assert_eq!(rendered_html, r#"<h1 class="title">Hello world!</h1>"#);

如果您仔细观察,会发现函数 Heading

  • 用大写声明的。在下面,它生成一个具有相同名称的结构体,并在其上实现 Render 特性。
  • 没有返回类型。这是因为出于性能原因,一切都被写入到writer中。

可见性与组件库

通常您可能希望将组件存储在项目树中的其他位置,而不仅仅是您正在工作的模块中(如果不是完全不同的模块)。在这些情况下,定义组件的函数的可见性将流向该结构体中的所有字段。

例如,如果我们在上面的Heading组件前添加 "pub"...

#[component]
pub fn Heading<'title>(title: &'title str) {
  rsx! { <h1 class={"title"}>{title}</h1> }
}

...生成的结构体会看起来像...

pub struct Heading {
  pub title: &'title str
}

从安全角度理解当构建您的库结构时,这一点很重要。

完整示例

#![feature(proc_macro_hygiene)]

// A simple HTML 5 doctype declaration
use render::html::HTML5Doctype;
use render::{
    // A macro to create components
    component,
    // A macro to compose components in JSX fashion
    rsx,
    // A macro to render components in JSX fashion
    html,
    // A trait for custom components
    Render,
};

// This can be any layout we want
#[component]
fn Page<'a, Children: Render>(title: &'a str, children: Children) {
   rsx! {
     <>
       <HTML5Doctype />
       <html>
         <head><title>{title}</title></head>
         <body>
           {children}
         </body>
       </html>
     </>
   }
}

// This can be a route in Rocket, the web framework,
// for instance.
pub fn some_page(user_name: &str) -> String {
    html! {
      <Page title={"Home"}>
        {format!("Welcome, {}", user_name)}
      </Page>
    }
}

许可证:MIT

依赖项

~1.5MB
~33K SLoC