将 Signals 引入 Lit Labs

新的 Signals 包将 TC39 Signals 提案与 Lit 集成

公布 @lit-labs/signals:将 TC39 Signals 提案与 Lit 集成

我们很高兴地宣布我们最新的 Lit Labs 包 @lit-labs/signals 发布,它将令人兴奋的 TC39 Signals 提案polyfill 直接与 Lit 集成。这个包提供了一种强大的响应式方式来管理 Web 应用中的状态,允许你使用共享的、可观察的 signals,当它们的值发生变化时自动更新组件。

Signals 正在迅速成为 JavaScript 生态系统的核心特性,TC39 提案有潜力统一 signals 以及我们在不同库和框架中管理状态和响应性的方式。

虽然该提案还处于早期阶段,但你今天就可以开始尝试 signals 和 @lit-labs/signals,看看基于通用响应式原语构建组件和应用对你来说效果如何。

什么是 Signals?

简单来说,signals 是持有值或计算结果的可观察数据结构。当 signal 的值发生变化时,你的应用中所有依赖它的组件或部分都会自动收到通知并更新。这在多个组件可能需要共享和响应状态变化的 UI 中特别有用。

基于标准的 Signals 的关键优势

  1. 共享可观察状态:Signals 非常适合管理跨多个组件共享的状态。如果一个组件更新了一个 signal,所有使用它的其他组件也会自动更新。
  2. 精确更新:Signals 实现了精确的重新渲染,通过仅处理 signal 值发生变化的绑定、跳过同一模板中的其他绑定,有可能提高性能。
  3. 互操作性:Signals 的标准化意味着不同的库和框架可以互操作地使用 signals,减少了对复杂适配器的需求并提高了兼容性。

为什么我们对 @lit-labs/signals 感到兴奋

Lit 以其轻量、高性能和声明式的 Web Components 构建方式而闻名。但 Lit 聚焦于帮助你构建可复用的、封装的_组件_。Lit 不是一个框架,也不会规定如何建模你的_数据_或使其可观察。

Lit 的响应性默认是相对_浅层_的。当组件自身的响应式属性发生变化时,组件会自动更新,但当嵌套属性发生变化时不会。响应深度属性变化一直需要手动请求更新,或者集成 Redux 或 MobX 等状态管理系统。

Signals 给了我们与这些状态管理系统相同的深度可观察能力,但使用了更小、更简单的 API,并且有潜力成为跨大量工具、组件和框架的通用标准。

Signals 对 Lit 来说并不完全是新的。我们之前发布了 @lit-labs/preact-signals 包,但我们对需要为特定的 signals 库构建 Lit 集成感到不太满意,而且可能需要为 Lit 开发者想要使用的每个 signals 库都构建一个。

JavaScript 中标准化的 signals 将让我们只需构建一个集成(并最终将 signals 支持直接添加到 Lit 的核心),并实现 signal 使用库之间的互操作,秉承 Web Components 所实现的互操作精神。

新的 @lit-labs/signals 包使得在你的 Lit 组件中使用新的 signals 提案变得非常简单。

让我们来看几个示例...

示例 1:共享计数器

这是一个使用 @lit-labs/signals 的共享计数器的简单示例。要在此组件中启用基于 signal 的响应性,我们只需在自定义元素定义中使用 SignalWatcher mixin;我们读取的任何 signals 都将被自动观察,当它们的值变化时触发更新:

import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {SignalWatcher, signal} from '@lit-labs/signals';

// 这是一个使用 signals polyfill 的标准 TC39 signal。
// 该 signal 在所有组件实例之间共享。
const count = signal(0);

@customElement('shared-counter')
export class SharedCounterComponent extends SignalWatcher(LitElement) {
render() {
// 只需在模板中使用 signal,当 signal 变化时
// 你的组件就会更新。
return html`
<p>The count is ${count.get()}</p>
<button @click=${this.increment}>Increment</button>
`;
}

increment() {
count.set(count.get() + 1);
}
}

使用这种方式,可以将任意数量的 <shared-counter> 组件添加到 DOM 中,所有组件都将反映相同的计数器值,在 signal 变化时自动更新。

你也可以在 Lit Playground 中查看此示例

示例 2:精确 DOM 更新

使用 watch 指令,我们还可以实现精确更新,针对单个绑定而不是整个组件:

import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {SignalWatcher, watch, signal} from '@lit-labs/signals';

const count = signal(0);

@customElement('pinpoint-counter')
export class PinpointCounter extends SignalWatcher(LitElement) {
render() {
return html`
<p>The count is ${watch(count)}</p>
<button @click=${this.increment}>Increment</button>
`;
}

increment() {
count.set(count.get() + 1);
}
}

这里,当 signal 变化时,只有显示计数的绑定会被处理,跳过不必要的工作并提高性能。

未来工作

这只是我们探索 Lit signals 集成的开始。很快,我们将在 @lit-labs/signals 中添加更多实用工具,用于从集合中增量渲染更改、在组件中轻松运行副作用,以及将 signals 用于响应式属性。

最终——随着提议的标准推进以及我们在 Lit 中使用 signals 的经验积累——signals 可能会成为核心库的一部分。

Lit 团队正在与 TC39 Signals 提案的冠军们密切合作,以确保 signals 能在 Lit、Web Components 和普通 DOM 用例中良好工作。我们怎么强调都不为过,signals 在形成可互操作的响应式原语方面有多么大的潜力,这种原语可以跨库和组件工作,无需集中式框架。

试用并提供反馈

我们渴望在你试用这个新包时获得社区的反馈。作为 Lit Labs 家族的一部分,@lit-labs/signals 仍然是实验性的,可能会根据用户反馈和 TC39 Signals 提案的进展而发生重大变化。

要开始使用,只需安装该包:

npm i @lit-labs/signals

我们很想听听你的想法,请试用它,并告诉我们它对你来说效果如何。

我们很期待看到你用 signals 构建什么,以及它们将如何塑造 Web 应用中状态管理的未来!

谢谢!

-Lit 团队

阅读更多文章