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 模板引擎
66 每月下载次数
在 render_macros 中使用
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><Hello /></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