介绍 Preact Signals

2022-09-17 23:48:50 浏览数 (2)

1. 什么是 Signals?

Signals 是用来处理状态的一种方式,它参考自 SolidJS,吸收了其大部分的优点。无论应用多么复杂,它都能保证快速响应。

Signals 的独特之处在于状态更改会以最有效的方式来自动更新组件和 UI。

Signals 基于自动状态绑定和依赖跟踪提供了出色的工效,并具有针对虚拟 DOM 优化的独特实现。

2. 为什么是 Signals?

2.1 状态管理的困境

随着应用越来越复杂,项目中的组件也会越来越多,需要管理的状态也越来越多。

为了实现组件状态共享,一般需要将状态提升到组件的共同的祖先组件里面,通过 props 往下传递,带来的问题就是更新时会导致所有子组件跟着更新,需要配合 memouseMemo 来优化性能。

虽然这听起来还挺合理,但随着项目代码的增加,我们很难确定这些优化应该放到哪里。

即使添加了 memoization,也常常因为依赖值不稳定变得无效,由于 Hooks 没有可以用于分析的显式依赖关系树,所以也没法使用工具来找到原因。

另一种解决方案就是放到 Context 上面,子组件作为消费者自行通过 useContext 来获取需要的状态。

但是有一个问题,只有传给 Provider 的值才能被更新,而且只能作为一个整体来更新,无法做到细粒度的更新。

为了处理这个问题,只能将 Context 进行拆分,业务逻辑又不可避免地会依赖多个 Context,这样就会出现 Context 套娃现象。

2.2 通向未来的 Signals

看到这里你一定感觉似曾相识,没错,通往未来的解决方案一定是我 —— Recoil,不对,这次的主角是 Signals。

signal 的核心是一个通过 value 属性 来保存值的对象。它有一个重要特征,那就是 signal 对象的值可以改变,但 signal 本身始终保持不变。

代码语言:javascript复制
import { signal } from "@preact/signals";

const count = signal(0);

// Read a signal’s value by accessing .value:
console.log(count.value);   // 0

// Update a signal’s value:
count.value  = 1;

// The signal's value has changed:
console.log(count.value);  // 1

在 Preact 中,当 signal 作为 props 或 context 向下传递时,传递的是对 signal 的引用。这样就可以在不重新渲染组件的情况下更新 signal,因为传给组件的是 signal 对象而不是它的值。

这让我们可以跳过所有昂贵的渲染工作,立即跳到任意访问 signal .value 属性的组件。

这里有 VDOM 和 Signals 在 Chrome 里面更新时的火焰图对比,可以发现 Signals 非常快。相比组件树更新,Signals 渲染会更快一些,这是因为更新状态图所需的工作要少得多。

Signals 具有第二个重要特征,即它们会跟踪其值何时被访问以及何时被更新。在 Preact 中,当 signal 的值发生变化时,从组件内访问 signal 的属性会自动重新渲染组件。

2.3 栗子

我们可以用一个例子来理解 Signals 的独特之处:

代码语言:javascript复制
import { signal } from "@preact/signals";

const count = signal(0);

const App = () => {
  return (
    <Fragment>
      <h1 onClick={() => count.value  ;}>
         
        {console.log("  ")}
      </h1>
      <span>{count}</span>
    </Fragment>
  );
};

当我们点击10次加号之后,count会从0变成10,那么" "是否会被打印10次呢?

从我们平时写 React 组件的经验来说,肯定会被打印10次,但在 Signals 里面不是这样。

从这个 Gif 可以看到," "一次都没被打印出来,这就是 Signals 的独特之处,整个组件没有被重新渲染。

不仅 h1 没有重新渲染,甚至连 span 节点都没有重新渲染,唯一更新的地方就只有 {count} 这个文本节点。

0 人点赞