#html #jsx #html-macro #node #attributes #map #axohtml

axohtml-macros

为Rust编写的类型检查JSX (proc_macro crate)

6个版本 (破坏性更新)

0.5.0 2023年3月30日
0.4.1 2023年1月25日
0.3.0 2023年1月2日
0.2.0 2022年12月19日
0.1.0 2022年12月16日

模板引擎中排名633

每月下载量40
3个Crates中使用(通过axohtml

MPL-2.0+

56KB
1.5K SLoC

axohtml

Github Actions Rust crates.io License: MPL 2.0

此crate提供了用于在Rust代码中构建完全类型检查的HTML文档的宏html!,使用与JSX兼容的语法。

此crate是Bodil Stokketyped-html crate的分支。选择分支而不是维护是因为目前没有计划使用或维护Wasm兼容性(目前)。

快速预览

let mut doc: DOMTree<String> = html!(
    <html>
        <head>
            <title>"Hello Axo"</title>
            <meta name=Metadata::Author content="Axo Developer Co."/>
        </head>
        <body>
            <h1>">o_o<"</h1>
            <p class="official">
                "The tool company for tool companies"
            </p>
            { (0..3).map(|_| html!(
                <p class="emphasis">
                    ">o_o<"
                </p>
            )) }
            <p class="citation-needed">
                "Every company should be a developer experience company."
            </p>
        </body>
    </html>
);
let doc_str = doc.to_string();

语法

此宏在很大程度上遵循JSX语法,但有一些差异

  • 文本节点必须加引号,因为Rust的标记化器只能处理有限数量的字符串字面量之外的标记。因此,您需要写<p>"Hello"/p>,而不是<p>Hello</p>。(如果忘记这样做,解析器将抛出一个错误。)
  • 元素属性可以接受简单的Rust表达式,但解析器有其局限性,因为它不是一个完整的Rust解析器。您可以使用字面量、变量、点属性、类型构造函数以及单个函数或方法调用。如果使用了解析器目前无法处理的表达式,它会报错。如果解析器不理解表达式,您可以在表达式中使用括号或大括号。您可以在大括号或括号块中使用任何Rust代码。

有效的HTML5

宏只接受有效的HTML5标签,没有标记为实验性或已废弃的标签或属性。如果它不接受您想要接受的内容,我们可以在pull request中进行讨论(特别是实验性标签和属性,大部分被省略,以节省篇幅,欢迎您实现它们)。

由于必须依赖于类型系统,结构验证是简化的:一些元素将有一个或多个必需的子元素,任何接受子元素的元素都将对子元素的类型有限制,通常是HTML规范中定义的广泛组。许多元素对子元素的子元素有限制,或要求可选元素有特定的顺序,但目前没有验证。

属性值

在属性值位置的大括号块应返回属性期望的类型。如果返回了不支持的类型,类型检查器会报错。您还可以使用字面量或几个简单的Rust表达式作为属性值(请参阅上面的语法部分)。

html! 宏会将一个 .into() 调用到值表达式上,这样您就可以使用任何为实际属性类型 A 定义的具有 Into<A> 特性的类型。

作为一个特殊情况,如果您使用字符串字面量,宏将使用 FromStr<A> 特性尝试将字符串字面量解析为期望的类型。这对于例如CSS类非常有用,让您可以键入 class="css-class-1 css-class-2" 而不必麻烦地构建一个 SpacedSet<Class>。这个大前提是:目前,宏无法在编译时验证字符串,如果字符串无效,转换将在运行时引发panic。

示例

let classList: SpacedSet<Class> = ["foo", "bar", "baz"].try_into()?;
html!(
    <div>
        <div class="foo bar baz" />         // parses a string literal
        <div class=["foo", "bar", "baz"] /> // uses From<[&str, &str, &str]>
        <div class=classList />             // uses a variable in scope
        <div class={                        // evaluates a code block
            SpacedSet::try_from(["foo", "bar", "baz"])?
        } />
    </div>
)

生成的节点

在子节点位置的大括号块应返回一个 IntoIteratorDOMTree。您可以返回单个元素或文本节点,因为它们都实现了对自己的 IntoIterator 实现方式。宏将在运行时消耗这个迭代器,并将生成的节点作为期望位置的子节点插入。

示例

html!(
    <ul>
        { (1..=5).map(|i| html!(
            <li>{ text!("{}", i) }</li>
        )) }
    </ul>
)

渲染

从宏输出的DOM树中实际产生有用内容,您有两种选择。

渲染到字符串

DOM树数据结构实现了Display,因此您可以在其上调用to_string(),将其渲染为String。如果您打算这样做,树的类型应该是DOMTree<String>,以确保您没有使用任何无法打印的事件处理程序。

let doc: DOMTree<String> = html!(
    <p>"Hello Axo"</p>
);
let doc_str = doc.to_string();
assert_eq!("<p>Hello Axo</p>", doc_str);

渲染到虚拟DOM

DOM树结构还实现了名为vnode()的方法,该方法将树渲染为VNode树,这是生成树的镜像,每个属性值都渲染为String。您可以遍历这个虚拟DOM树,并将其传递给您最喜欢的虚拟DOM系统。

许可证

此软件受Mozilla公共许可证第2.0版条款的约束。如果此文件未附带MPL副本,您可以在http://mozilla.org/MPL/2.0/获得一个副本。

版权所有 2018 Bodil Stokke,2022 Axo Developer Co.

依赖项

~0.5–9MB
~68K SLoC