响应式状态(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,它是一个只能被读取而不能写入的信号。该Signal类型会自动解引用为ReadSignal,因此你可以在任何需要 ReadSignal的地方 使用 Signal 。

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) }
}

事件处理程序

标签: none

添加新评论