9 个不稳定版本 (3 个破坏性更新)
0.4.3 | 2021 年 4 月 1 日 |
---|---|
0.4.2 | 2021 年 4 月 1 日 |
0.4.1 | 2021 年 3 月 31 日 |
0.3.1 | 2021 年 3 月 16 日 |
0.1.1 | 2021 年 3 月 7 日 |
#209 在 #reactive
每月 29 次下载
用于 maple-core
16KB
384 行代码
maple
一个无 VDOM、细粒度响应式的 Web 库。
入门
推荐的构建工具是 Trunk。首先,将 maple-core
添加到您的 Cargo.toml
maple-core = "0.4.3"
将以下内容添加到您的 src/main.rs
文件中
use maple_core::prelude::*;
fn main() {
let root = template! {
p {
"Hello World!"
}
};
render(|| root);
}
就这样!您已经使用 maple
创建了一个 Hello World 程序。要运行应用程序,只需运行 trunk serve --open
并在您的网络浏览器中查看结果。
template!
宏
maple
使用 template!
宏作为创建复杂用户界面的便捷方式。
// You can create nested elements.
template! {
div {
p {
span { "Hello " }
strong { "World!" }
}
}
};
// Attributes (including classes and ids) can also be specified.
template! {
p(class="my-class", id="my-paragraph", aria-label="My paragraph")
};
template! {
button(disabled="true") {
"My button"
}
}
// Events are attached using the `on:*` directive.
template! {
button(on:click=|_| { /* do something */ }) {
"Click me"
}
}
响应式
与依赖虚拟 DOM (VDOM) 不同,maple
使用细粒度响应式来保持 DOM 和状态同步。实际上,maple
的响应式部分可以单独使用,而不依赖于 DOM 渲染部分。
响应式基于响应式原语。以下是一个示例
use maple_core::prelude::*;
let state = Signal::new(0); // create an atom with an initial value of 0
如果您熟悉 React hooks,这会立即显得很熟悉。
现在,要访问状态,我们像这样在 state
上调用 .get()
方法
println!("The state is: {}", state.get()); // prints "The state is: 0"
要更新状态,我们在 state
上调用 .set(...)
方法
state.set(1);
println!("The state is: {}", state.get()); // should now print "The state is: 1"
这有什么用?这很有用,因为它提供了一种轻松通知任何状态变化的方法。例如,如果我们想打印出每个状态变化,可以轻松地这样完成
let state = Signal::new(0);
create_effect(cloned!((state) => move || {
println!("The state changed. New value: {}", state.get());
})); // prints "The state changed. New value: 0" (note that the effect is always executed at least 1 regardless of state changes)
state.set(1); // prints "The state changed. New value: 1"
state.set(2); // prints "The state changed. New value: 2"
state.set(3); // prints "The state changed. New value: 3"
如何让 create_effect(...)
函数在状态变化时执行闭包?调用 create_effect
会创建一个新的 “响应式作用域”,在这个作用域内调用 state.get()
会将自身添加为一个 依赖。现在,当调用 state.set(...)
时,它会自动调用所有依赖项,在这种情况下,调用 state
因为它是在闭包内部被调用的。
那个
cloned!
宏是做什么的?
cloned!
宏是一个用于将变量克隆到后续表达式的实用宏。之前的create_effect
函数调用完全可以写成以下形式create_effect({ let state = state.clone(); move || { println!("The state changed. New value: {}", state.get()); } }));
这最终只是一个权宜之计,直到 Rust RFC #2407 发生某些事情。
我们还可以轻松地使用 create_memo(...)
创建派生状态,它实际上是 create_effect
的一个易于使用的包装器。
let state = Signal::new(0);
let double = create_memo(cloned!((state) => move || *state.get() * 2));
assert_eq!(*double.get(), 0);
state.set(1);
assert_eq!(*double.get(), 2);
create_memo(...)
会自动在依赖项变化时重新计算派生值。
现在您已经了解了 maple
的响应式系统,我们可以看看如何使用它来更新 DOM。
使用响应式进行 DOM 更新
响应式自动集成到 template!
宏中。假设我们有以下代码
use maple_core::prelude::*;
let state = Signal::new(0);
let root = template! {
p {
(state.get())
}
}
这将扩展成类似以下内容
use maple_core::prelude::*;
use maple_core::internal;
let state = Signal::new(0);
let root = {
let element = internal::element(p);
let text = internal::text(String::new() /* placeholder */);
create_effect(move || {
// update text when state changes
text.set_text_content(Some(&state.get()));
});
internal::append(&element, &text);
element
}
如果我们在我们代码的某个地方调用 state.set(...)
,文本内容将自动更新!
组件
maple
中的组件是简单的函数,它们返回 HtmlElement
。它们通过函数参数接收它们的属性。
为了使组件能够自动响应属性变化,它们应该接受一个类型为 StateHandle<T>
的属性,并在 template!
中调用该函数来订阅状态。
为 Signal<T>
获取一个 StateHandle<T>
非常简单。只需调用 .handle()
方法。
这是一个简单组件的示例
// This is temporary and will later be removed.
// Currently, the template! macro assumes that all components start with an uppercase character.
#![allow(non_snake_case)]
use maple_core::prelude::*;
fn Component(value: StateHandle<i32>) -> TemplateResult {
template! {
div(class="my-component") {
"Value: " (value.get())
}
}
}
// ...
let state = Signal::new(0);
template! {
Component(state.handle())
}
state.set(1); // automatically updates value in Component
贡献
问题报告和 PR 受欢迎!通过 ARCHITECTURE.md 熟悉项目结构。
依赖项
~1.5MB
~34K SLoC