12个版本 (破坏性更改)
0.9.0 | 2023年5月26日 |
---|---|
0.7.1 | 2023年4月18日 |
0.6.0 | 2023年3月28日 |
0.4.0 | 2022年6月30日 |
0.1.0 | 2021年8月8日 |
#1318 in WebAssembly
用于 2 个crate(通过 kobold)
125KB
3K SLoC
Kobold
简单的声明式Web界面。
关键特性
- 使用类似HTML语法的声明式宏
view!
,包含可选的关闭标签。 - 具有可选参数的函数式组件。
- 状态管理和事件处理。
- 高性能,在Rust生态系统中的Wasm占用空间始终最低。
零成本静态HTML
view!
宏生成不透明的 impl View
类型,默认不进行分配。所有静态 DOM 元素编译成内联JavaScript代码,用于构建它们。表达式在首次渲染时注入到构建的DOM中。Kobold跟踪这些表达式的DOM节点引用。
由于表达式评估的确切类型是Rust编译器所知的,更新调用可以通过值(或指针)进行diff,并在它们更改时进行外科手术式的DOM更新。更改字符串或整数仅更新渲染该字符串或整数的确切 Text
节点。
如果 view!
宏调用包含没有表达式的DOM元素,则构建的 View
类型将为零大小,其 View::update
方法将为空,使静态DOM的更新实际上零成本。
你好,世界!
Kobold 中的组件通过在 渲染函数 上使用 #[component]
属性来创建。
use kobold::prelude::*;
#[component]
fn Hello(name: &str) -> impl View + '_ {
view! {
<h1>"Hello "{ name }"!"</h1>
}
}
fn main() {
kobold::start(view! {
<Hello name="Kobold" />
});
}
组件函数必须返回一个实现了 View
特性的类型。由于 view!
宏生成的是临时的本地定义类型,因此最佳做法是始终使用不透明的 impl View
返回类型。
这里所有内容都是静态类型的,宏在操作标记流时不会删除任何信息,因此 Rust 编译器可以告诉您何时出错。
error[E0560]: struct `Hello` has no field named `nam`
--> examples/hello_world/src/main.rs:12:16
|
12 | <Hello nam="Kobold" />
| ^^^ help: a field with a similar name exists: `name`
您甚至可以使用 rust-analyzer 重构组件或字段名称,它将为您更改宏内的调用。
状态管理
可以使用 stateful
函数创建拥有并操作其状态的视图。
use kobold::prelude::*;
#[component]
fn Counter(init: u32) -> impl View {
stateful(init, |count| {
bind! { count:
// Create an event handler with access to `&mut u32`
let onclick = move |_event| *count += 1;
}
view! {
<p>
"You clicked the "
// `{onclick}` here is shorthand for `onclick={onclick}`
<button {onclick}>"Button"</button>
" "{ count }" times."
</p>
}
})
}
fn main() {
kobold::start(view! {
<Counter init={0} />
});
}
stateful
函数接受两个参数
- 实现
IntoState
特性的状态构造函数。 Kobold 为大多数原始类型提供了默认实现,因此我们在这里可以使用u32
。 - 使用构造的状态的匿名渲染函数,在我们的例子中其参数是
&Hook<u32>
。
这里的 Hook
是指向状态的智能指针,允许对状态进行非可变访问。可以为任何 Hook
调用 bind!
宏来创建具有对底层状态的 &mut
引用的闭包。
有关更多信息,请访问 stateful
模块文档。
可选参数
使用 #[component(<param>?)]
语法将组件参数设置为默认值。
// `code` will default to `200` if omitted
#[component(code?: 200)]
fn Status(code: u32) -> impl View {
view! {
<p> "Status code was "{ code }
}
}
view! {
// Status code was 200
<Status />
// Status code was 404
<Status code={404} />
}
有关更多信息,请访问 #[component]
宏文档。
条件渲染
由于 view!
宏生成唯一的临时类型,因此调用该宏的 if
或 match
表达式将无法编译。
在 #[component]
属性上使用 auto_branch
标志时,Kobold 将扫描组件渲染函数的主体,并将所有位于 if
或 match
表达式内的 view!
宏调用包装在一个枚举中,使它们具有相同的类型。
#[component(auto_branch)]
fn Conditional(illuminatus: bool) -> impl View {
if illuminatus {
view! { <p> "It was the year when they finally immanentized the Eschaton." }
} else {
view! { <blockquote> "It was love at first sight." }
}
}
有关更多信息,请访问 branching
模块文档。
列表和迭代器
要渲染迭代器,请使用 for
关键字。
// `ListIteratorExt` is included in the prelude
use kobold::prelude::*;
#[component]
fn IterateNumbers(count: u32) -> impl View {
view! {
<ul>
{
for (1..=count).map(|n| view! { <li> "Item #"{n} })
}
}
}
在更新时,迭代器只会被消费一次,所有项目将与上一个版本进行比较。在更新此类列表时,除非渲染的列表需要超过其原始容量,否则 Kobold 不会进行任何分配。
有关关键字的更多信息,请访问 keywords
模块文档。
借用值
View
类型实际上是瞬时的,只需要在初始渲染期间或后续更新期间存在。这意味着你可以轻松且低成本地渲染借用状态,而无需不必要的克隆
#[component]
fn Users<'a>(names: &'a [&'a str]) -> impl View + 'a {
view! {
<ul>
{
for names.iter().map(|name| view! { <li> { name } })
}
}
}
具有子组件的组件
如果你希望从父 view!
调用中捕获子组件,只需将 #[component]
更改为 #[component(children)]
use kobold::prelude::*;
#[component(children)]
fn Header(children: impl View) -> impl View {
view! {
<header><h1>{ children }</h1></header>
}
}
fn main() {
kobold::start(view! {
<Header>"Hello Kobold"</Header>
});
}
你可以更改参数的名称,甚至将其设置为具体的
use kobold::prelude::*;
// Capture children into the argument `n`
#[component(children: n)]
fn AddTen(n: i32) -> i32 {
// integers implement `View` so they can be passed by value
n + 10
}
fn main() {
kobold::start(view! {
<p>
"Meaning of life is "
<AddTen>{ 32 }</AddTen>
</p>
});
}
更多示例
要运行 Kobold,你需要安装 trunk
(如果你遇到问题,请检查完整说明)
cargo install --locked trunk
你可能还需要将 Wasm 目标添加到 Rust
rustup target add wasm32-unknown-unknown
然后只需运行一个示例
## Go to an example
cd examples/todomvc
## Run with trunk
trunk serve
致谢
- Pedrors 提供的 Kobold 标志。
许可证
Kobold 是免费软件,并按照 Mozilla 公共许可证 第 2.0 版发布。请参阅 LICENSE。
依赖关系
~170KB