sycamore状态
响应式状态(Reactive state)由响应式节点(reactive nodes)构建而成。响应式节点最简单的例子就是信号。它可以被认为是一个类型的简单包装器,用于跟踪何时被访问以及何时被更新。要创建新的信号,请使用create_signal函数。
let signal = create_signal(123);
信号内部的值可以读取或写入。默认情况下,该Signal::get方法会复制信号内部的值,因此包装类型必须实现Copy。如果您有不可复制的类型,则可以改用Signal::get_clone。
let signal = create_signal(123);
// Should print `123`.
console_log!("{}", signal.get());
// Update the signal with a new value.
signal.set(456);
// Should print `456`.
console_log!("{}", signal.get());
// `Signal<T>` also implements `Display` so this is the same as the above.
console_log!("{signal}");
我们在这里使用console_log!而不是println!
因为在wasm32-unknown-unknown target上,Rust 标准库无法访问 Web API,因此println!不会执行任何操作。console_log!宏会调用javascript的console.log将值打印到 JavaScript 控制台的函数。
Signal::set 只接受普通(不可变)引用,而不是可变引用。这也是 Rust中的cell 类型的一个示例,提供了内部可变性。
effect 是每次其依赖项之一更新时都会被调用的函数。每当在函数内部使用依赖项时,依赖项都会被自动跟踪。
let signal = create_signal(123);
create_effect(move || {
// Using `.get(...)` automatically tracks this signal as a dependency.
let value = signal.get();
console_log!("{value}");
// Or we can use the shorter: `console_log!("{signal}");`
});
signal.set(456);
signal.set(789);
这将在终端上打印以下内容:
123
456
789
Memos 和派生状态
memos 存储派生信号的值,并且仅在其依赖项发生变化时更新该值。
let signal = create_signal(1);
let derived = create_memo(move || expensive_computation(signal.get()));
// The memo will only call the closure once so long as `signal` hasn't changed.
let foo = derived.get();
let bar = derived.get();
// This also triggers `derived` to recompute its value.
signal.set(2);
create_memo钩子返回一个ReadSignal
effects实际上是通过memos实现的。从语义上讲,这并不完全正确,因为我们使用效果来创建 side-effects,而使用memos来执行纯计算。然而,从实现的角度来看,effect仅仅是一个不返回值的memo。
响应式视图
现在我们已经准备好了所有这些响应式机制,让我们看看如何使用它来管理应用的状态。我们想要做的是将状态存储在响应式节点中,然后让 UI 自动与状态同步。
Sycamore 让这一切变得极其简单。只需在view!宏中插入一个片段,访问响应式状态,UI 就会自动更新以适应任何状态变化。
let counter = create_signal(1);
view! {
p { "Count: " (counter) }
}
其本质上是这样的:
let counter = create_signal(1);
let text1 = document().create_text_node("Count: ");
let text2 = document().create_text_node(counter.get().to_string());
create_effect(move || {
// This effect is automatically run whenever `counter` is changed.
text2.set_text_content(counter.get().to_string());
});
我们当然也可以在视图中使用派生状态:
let counter = create_signal(1);
let doubled = create_memo(move || counter.get() * 2);
view! {
p { "Count: " (counter) }
p { "Doubled: " (doubled) }
}
事件处理程序