将 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 的关键优势
- 共享可观察状态:Signals 非常适合管理跨多个组件共享的状态。如果一个组件更新了一个 signal,所有使用它的其他组件也会自动更新。
- 精确更新:Signals 实现了精确的重新渲染,通过仅处理 signal 值发生变化的绑定、跳过同一模板中的其他绑定,有可能提高性能。
- 互操作性: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 变化时自动更新。
示例 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 我们很想听听你的想法,请试用它,并告诉我们它对你来说效果如何。
- 初步文档可在 lit.tips/docs/data/signals/ 获取
- 你可以在 GitHub 反馈讨论中分享反馈
- 在 Lit monorepo issues 中报告问题
- 加入我们的 Discord 服务器聊天!
我们很期待看到你用 signals 构建什么,以及它们将如何塑造 Web 应用中状态管理的未来!
谢谢!
-Lit 团队