
我曾经谈到我认为signals是Web框架的未来。自那以后,我很高兴地欢迎Angular加入了Signals俱乐部。
令我惊讶的是,安德鲁·克拉克(Andrew Clark)在一条推文中写道,他认为React Forget比Signals是一个更好的方法。
React优化编译器,又名React Forget
React Forget是一个实验性项目,旨在通过自动生成等效的useMemo和useCallback调用来改善React应用程序的渲染性能,以最小化重新渲染的成本,同时保留React的编程模型。
Memoization 与signals
对于大多数情况来说,signals不需要记忆。 React Forget自动在所有地方插入记忆,所以我可以理解为什么有人会认为 Memoization与signals是相同的。
让我向你展示 Memoization方法失败而signals卓越的地方。
示例
让我们构建一个经典的单页应用程序,包含一个购买按钮和一个购物车。选择此示例是因为它展示了一个现实世界的场景,其中状态、变化和渲染被分离到不同的组件中:
- 声明状态的地方(购物车内容)
- 变异状态的地方(购买按钮)
- 渲染状态的地方(购物车UI)

使用Memoization和prop drilling的React
要理解的关键是,我们需要将状态(以及setter)传递到需要它的组件中。
在我们的例子中,这是ShoppingCart和BuyButton组件。这意味着我们必须通过许多层组件进行prop drilling才能到达那里(上下文是另一种方法,但结果相同)。
现在让我们看看当我们尝试改变购物车时会发生什么:
BuyButton有一个setCart setter,它使用购物车的新状态调用。- 调用
setCart会使App组件失效。 React从App组件开始重新渲染。通常情况下,这会导致大多数子组件重新渲染,但让我们假设React Forget已经将Memoization最大化。在这种情况下,React仍然需要渲染App和ShoppingCart之间的所有组件。在我们的简单情况中,只有Header和User,但在真实的应用程序中会有更多。
即使React Forget编译器Memoization了一切,它仍然无法避免的事实是,当状态发生变化时,它必须从状态存储的地方通过prop drilling一直传递到需要的地方。
重新渲染仅用于prop drilling的中间组件是一种浪费。
signals
现在让我们看看signals。数据流图与以前完全相同,只是现在我们不是传递状态,而是通过许多层组件传递信号到ShoppingCart和BuyButton。
现在让我们看看当我们尝试改变购物车时会发生什么。
BuyButton有一个setCart信号,它使用购物车的新状态调用。- 信号通知
ShoppingCart更新购物车的状态。
就是这样。
请注意,即使状态是在App组件中声明的,但App并不是重新渲染过程的一部分,而且所有通过prop drilling将其传递给ShoppingCart的组件也都不是。
这就是signals的强大之处所在。
signals > useMemo()
这就是我们说,通常情况下,signals不需要useMemo()的原因。signals使您能够仅对需要更新的UI执行精细的更新,从而跳过所有中间部分。
useMemo()无法跳过组件层次。在最好的情况下,useMemo()可以修剪组件树,使得需要访问的分支更少,但更新仍然需要我们通过这些分支进行。
结论
signals和Memoization可能是等效的,但实际上,signals要更高效得多,因为它们不受组件渲染树的限制。
它们存在于与组件树独立的平面上。
事实上,signals不需要Memoization,但在 React中的Memoization无法与signals的精确性相匹配。