JavaScript Signals 的演变
最近,“ Signals ”一词在前端世界中引起了不小的讨论。 在看似很短的时间内,它们好像在很多前端框架中都有出现,从 Preact 到 Angular
但它并不是一个新事物。我们可以追溯到20世纪60年代末的研究。在其基础上,使第一个电子表格和硬件描述语言(如 Verilog 和 VHDL )得以实现的相同建模。
甚至在 JavaScript 中,自从声明式 JavaScript 框架诞生以来,我们就拥有了 Signal。随着时间的推移,它们已经有了不同的名字,并在这些年里不断地流行起来。但现在我们又来到了这里,现在是一个很好的时机,让我们对其产生的原因以及如何使用有更多的了解。
开端
有时我们会惊讶地发现,多个团队在完全相同的时间内达成了类似的解决方案。在声明式 JavaScript 框架的起步阶段,有 3 个方案在 3 个月内相继发布。Knockout.js(2010年7月)、Backbone.js(2010年10月)、Angular.js(2010年10月)。
Angular 的「脏值检查」,Backbone 的 「模型驱动重复渲染」,Knockout 的「细粒度更新」。每个方案都有些许不同,但最终都成为了今天我们更新 state 和管理 DOM 的基础。
Knockout.js 对本文的主题特别重要,因为它的「细粒度更新」是建立在我们称之为 Signals 的基础上的。他们最初引入了两个概念:observable
(状态)和 computed
(副作用),并且在接下来的几年里在业界引入第三个概念 pureComputed
(衍生状态)。
const count = ko.observable(0);
const doubleCount = ko.pureComputed(() => count() * 2);
// doubleCount 更新时执行 console.log
ko.computed(() => console.log(doubleCount()))
狂野大西部
这些模式是在服务端 MVC 开发和过去几年的 jQuery 中学到的混合模式。其中一个特别常见的模式叫做数据绑定
,Angular.js 和 Knockout.js 都有,尽管实现方式略有不同。
数据绑定
是将部分状态(state)附加到视图树(view tree)某个特定部分的一个方法。可以做到的一个强大的事情是使其成为双向的。因此,我们可以让状态更新 DOM,反过来,DOM 事件自动更新状态,所有这些都是以一种简单的声明方式进行的。
然而,如果滥用这种能力,最终会搬起石头砸自己的脚。在 Angular 中,如果不知道有什么变化,就会对整个树进行肮脏的检查,向上传播可能会导致它发生多次。在 Knockout 中,由于你在树上来回走动,所以很难跟踪变化的路径,循环是很常见的。
无障碍
随之而来的是 React 的大规模采用。一些人仍然喜欢响应式模型,而且由于 React 对状态管理没有太多的限制,所以很有可能将两者混合起来。
Mobservable(2015)就是这种解决方案。但比起与 React 合作,它带来了新的东西。它强调一致性和无障碍传播。也就是说,对于任何给定的变化,系统的每一部分都只运行一次,而且是以适当的顺序同步运行。
它通过将先前方案中典型的基于 push 的响应式换成 push-pull 混合系统来做到这一点。变化的通知被推送出去,但派生状态的执行被推迟到读取它的地方。
虽然这个细节在很大程度上被一个事实所掩盖,即无论如何 React 都会重新渲染读取变化的组件,但这为这些系统可调试性和一致性方面的提升迈出了重要一步。在接下来的几年里,随着算法的不断完善,我们会看到一个趋势在不断的完善。
征服泄漏的观察者
细粒度的响应性是 观察者模式 的一个变种。虽然这是一个强大的同步模式,但它也有一个典型的问题。一个 Signal 会保留对其订阅者的强引用,所以一个长时间存在的 Signal 会保留所有的订阅,除非是手动处理掉。
这种方式在大量使用时变得非常复杂,尤其是涉及嵌套的时候。在处理分支逻辑和树时,嵌套是很常见的,就像你在构建用户界面视图那样。
一个不太知名的库,S.js(2013),提供了答案。S.js 是独立于其他大多数解决方案而开发的,它更直接地以数字电路为模型,所有的状态变化都在时钟周期内进行。它将其状态基元称为 "Signals(信号)"。虽然不是第一次使用这个名字,但它是我们今天使用的术语的来源。
更重要的是,它引入了响应式所有权的概念。一个所有者将收集所有的子响应式作用域,并在所有者自己的 deposal 逻辑或在重新执行时管理它们的 deposal 逻辑。响应式视图将从一个根所有者开始,然后每个节点将作为其后代的所有者。这种所有者模式不仅对 deposal 很有用,而且是在响应式视图中建立 Provider / Consumer 上下文的一种机制。
调度
Vue (2014) 也为今天的发展提供了巨大的贡献。除了在优化一致性方面与 MobX 保持一致外,Vue从一开始就将「细粒度」的响应性作为其核心。
虽然 Vue 与 React 共享虚拟 DOM 的使用,但响应性是一流的,这意味着它首先作为一种内部机制与框架一起开发,以支持其 Options API,并在过去几年中,成为 Composition API 的核心 (2020)。
Vue 通过调度任务,将 pull / push 机制向前推进了一步。默认情况下,Vue 的修改不会立马被执行,而是要等到下一个微任务才会执行。
然而,这种调度也可以用来做一些其他的事情,比如 keep-alive
(在没有计算成本的情况下保留屏幕外的图形),以及 Suspense
。甚至像 并发渲染 这样的事情也可以用这种方法来实现,真正展示了如何获得基于 pull 和 push 的两种方法的最佳效果。
编译
2019年,Svelte 3 向大家展示了我们可以用一个编译器做多少事情。事实上,他们把响应性完全编译掉了。这并非没有取舍,但更有趣的是,Svelte 向我们展示了一个编译器如何能抚平人体工程学的缺点。而这将继续成为前端的一个趋势。
响应性语言的特性:状态、派生状态和副作用;不仅为我们提供了描述用户界面等同步系统所需的一切,而且是可分析的。我们可以准确地知道什么地方发生了什么变化。可追溯性的潜力是深远的。
如果我们在编译时知道这一点,我们就可以少发一些 JavaScript 。我们可以在代码加载方面更加自由。这就是 Qwik 和 Marko 的可恢复性的基础。
通往未来的 Signals
鉴于这项技术有多老,说还有很多东西需要探索,可能会让人惊讶。这是因为它是一种解决方案的建模方式,而不是一种特定的技术。它所提供的是一种描述状态同步的语言,与你要让它执行的任何副作用无关。
那么,它被 Vue、Solid、Preact、Qwik 和 Angular 采用似乎也就不足为奇了。我们已经看到它进入了 Rust 的 Leptos 和 Sycamore,表明 DOM 上的 WASM 不一定很慢。它甚至被 React 考虑在引擎使用。
也许这很合适,因为 React 的 虚拟DOM 始终只是一个实现细节。
Signals 和响应性语言似乎是事情的交汇点。但这在其第一次进入 JavaScript 的时候并不那么明显。也许这是因为 JavaScript 并不是最适合它的语言。
无论这一切的结局是什么,到目前为止都是一次相当不错的旅程。有这么多人关注 Signals,我迫不及待地想知道我们的下一步会是什么。
总结
以上简单介绍了 Javascript 中 Signals 的演变,希望对正在学习前端的你有所帮助。当然,这并不是所有的内容,后续我还会一直更新这篇文章,从更多方面去探讨前端中的 Signals。最后感谢大家对本文的支持~欢迎点赞收藏,在评论区留下你的高见 🌹🌹🌹
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanffaej
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24