大家好,我是卡颂。
你是否很讨厌调用顺序的限制(不能写在条件语句里)?
你是否遇到过在中使用了某个,又忘记将其加入,导致回调执行时机出问题?
怪自己粗心?怪自己不好好看文档?
答应我,不要怪自己。
根本原因在于没有将实现为响应式更新。
是实现难度很高么?本文会用50行代码实现无限制版,其中涉及的知识也是、等基于响应式更新的库的底层原理。
本文的正确食用方式是收藏后用电脑看,跟着我一起敲代码(完整在线链接见文章结尾)。
手机党要是看了懵逼的话不要自责,是你食用方式不对。
注:本文代码来自Ryan Carniato的文章Building a Reactive Library from Scratch,老哥是作者
万丈高楼平地起
首先来实现:
返回值数组第一项负责取值,第二项负责赋值。相比,我们有个小改动:返回值的第一个参数是个函数而不是本身。
使用方式如下:
没有黑魔法
接下来实现,包括几个要点:
依赖的改变,回调执行
不需要显式的指定依赖项(即中的第二个参数)
举个例子:
变化后第一个会执行回调(因为他内部依赖),但是第二个不会执行。
前端没有黑魔法,这里是如何实现的呢?
答案是:订阅发布。
继续用上面的例子来解释订阅发布关系建立的时机:
当定义后他的回调会立刻执行一次,在其内部会执行:
执行时会建立与之间订阅发布的关系。
当下次执行(setter)时会通知订阅了变化的,执行其回调函数。
数据结构之间的关系如图:
每个内部有个集合,用来保存订阅该state变化的。
是每个对应的数据结构:
其中:
:该的回调函数
:该依赖的对应的集合
我知道你有点晕。看看上面的结构图,缓缓,咱再继续。
实现useEffect
首先需要一个栈来保存当前正在执行的。这样当调用时才知道应该与哪个建立联系。
举个例子:
执行时需要知道自己处在的上下文中(而不是),这样才能与建立联系。
接下来实现,包括如下功能点:
每次回调执行前重置依赖(回调内部的会重建依赖关系)
回调执行时确保当前处在栈顶
回调执行后将当前从栈顶弹出
代码如下:
用来移除该与所有他依赖的之间的联系,包括:
订阅关系:将该订阅的所有变化移除
依赖关系:将该依赖的所有移除
移除后,执行回调会再逐一重建关系。
改造useState
接下来改造,完成建立订阅发布关系的逻辑,要点如下:
调用时获取当前上下文的,建立关系
调用时通知所有订阅该变化的回调执行
的实现,同样包括2个关系的建立:
让我们来试验下:
实现useMemo
接下来基于已有的2个实现:
自动依赖跟踪
这套50行的还有个强大的隐藏特性:自动依赖跟踪。
我们拓展下上面的例子:
现在我们有3个:、、。
作为,依赖以上三个。
最后,当变化时,会触发回调。
当以上代码运行后,基于初始的3个,会计算出,进而触发回调,打印:
接下来调用:
下面的事情就有趣了,当调用:
并没有打印。
这是因为当导致为后,进入如下逻辑:
由于没有执行,所以与已经没有订阅发布关系了!
只有当后,进入如下逻辑:
此时才会重新依赖与。
自动的依赖跟踪,是不是很酷~
总结
至此,基于订阅发布,我们实现了可以自动依赖跟踪的无限制。
这套理念是最近几年才有人使用么?
早在2010年初就用这种细粒度的方式实现响应式更新了。
不知道那时候,Steve Sanderson(作者)有没有预见到10年后的今天,细粒度更新会在各种库和框架中被广泛使用。
突破Hooks所有限制,只需50行代码
大家好,我是卡颂。 你是否很讨厌调用顺序的限制(不能写在条件语句里)? 你是否遇到过在中使用了某个,又忘记将其加入,导致回调执行时机出问题? 怪自己粗心?怪自己不好好看文档? 答应我,不要怪自己。 根本原因在于没有将实现为响应式更新。 是实现难
本文来自网络,不代表站长网立场,转载请注明出处:https://www.tzzz.com.cn/html/xinwen/dongtai/2021/1107/21659.html