2 个不稳定版本
0.2.0 | 2020 年 7 月 5 日 |
---|---|
0.1.0 | 2020 年 1 月 23 日 |
#7 in #vue
每月 46 次下载
82KB
1.5K SLoC
Reax
Reax 是一个为 Rust 提供的函数之间依赖推断的反应性系统。
Reax 管理的每个 Variable
都是一个依赖图中的一个节点。变量的更改和使用由全局的 ThreadRuntime
跟踪,该全局变量会更新图以反映实际的变量访问。存在两种内置变量
Var
可以显式地被修改。ComputedVar
使用函数懒加载地计算其值。
用户可以使用 .watch
监听和响应变量的更改。
关键的是,一个 ComputedVar
只会在需要时重新计算。如果在计算其值时,一个 ComputedVar
在任何地方(直接或间接地)使用了任何其他变量,这些上游变量的任何更改都会自动将计算变量标记为脏,并在下次使用时重新计算变量的值。
示例
Reax 会构建一个模型来描述程序运行时变量之间的交互。
use reax::prelude::*;
// Create input variables.
let number = Var::new(1).with_label("number");
let name = Var::new("Sam").with_label("name");
// Create computed variables.
let formatted = (&number)
.map(|x| format!("{}", x))
.with_label("formatted");
let printout = computed! {
output! text = String::new(); // Reuse a buffer
text.clear();
*text += *name.get();
*text += " sees ";
*text += formatted.get().as_str();
}.with_label("printout");
// The computed variables haven't been used yet. Nothing is hooked-up.
assert_eq!(printout.node().depends_on(formatted.node()), false);
// Use the variables!
assert_eq!(printout.get().as_str(), "Sam sees 1");
number.set(42);
name.set("Reax");
assert_eq!(printout.get().as_str(), "Reax sees 42");
// Reax now knows how data moves through the variables!
assert_eq!(printout.node().depends_on(formatted.node()), true);
// Print a .dot visualization.
reax::ThreadRuntime::write_graphviz(std::io::stdout().lock()).unwrap();
我们可以通过 Reax 的视角来查看这个示例
Reax 只会在需要时更新计算变量。
use reax::prelude::*;
let number = Var::new(0);
let bigger_number = (&number).map(|x| *x + 10);
let even_bigger_number = (&bigger_number).map(|x| *x + 100);
let times_called = Var::new(0);
// Set up a watcher to track how often bigger_number changes.
let mut eval = EagerCompute::new(());
eval.watch(&bigger_number, |_| {
*times_called.mutate() += 1;
});
// The watcher is called once on creation.
assert_eq!(*times_called.get(), 1);
// Run the watcher. This is effectively a no-op since nothing has changed.
for _ in 0..100 { eval.tick(); }
// Update a variable.
number.set(1);
// Dependent variables are instantly dirty.
assert_eq!(bigger_number.node().is_dirty(), true);
assert_eq!(even_bigger_number.node().is_dirty(), true);
// Run the watcher again. This time it fires.
eval.tick();
assert_eq!(*times_called.get(), 2);
// even_bigger_number is still dirty since no one has used it yet.
assert_eq!(even_bigger_number.node().is_dirty(), true);
在这里,你可以看到当 number
改变时,它下游的所有变量都会立即被标记。但它们不会在未被使用之前重新计算。
Reax 没有内置对集合的理解,因此你可以使用嵌套的 Var
来更好地控制更改的“深度”。
use reax::prelude::*;
// Create a list of variables.
let list = Var::new(Vec::new());
for x in 1..=3 {
list.mutate().push(Var::new(x));
}
// Some computed properties:
let length = computed! { list.get().len() };
let sum = computed! {
list.get().iter().map(|elem| *elem.get()).sum::<i32>()
};
// Make length and sum outdated by pushing an extra element.
list.mutate().push(Var::new(4));
// Update the length.
length.check(&mut ());
// Now only make sum outdated, and leave it that way.
list.get()[0].set(100);
在示例的最后,可视化运行时,你可以看到只有总和是脏的。列表元素本身没有任何依赖,因此任何对它们的更改都不会影响从未读取过它们的变量。Reax 也没有看到额外元素将被用于总和。它将在下一次计算总和时发现这一点。