Recoil 源码探索
Recoil 源码探索
作者:伍然今天,由伍然带来《Recoil 源码探索》的分享,让我们一起学起来,造起来!
1. 简介 Recoil 是 facebook 推出的一个状态解决方案,主要的特点有:1. 高性能的渲染,2. 通过状态生成派生值。
2. 特点 高性能渲染
文章图片
在 List 和 Canvas 中的第二个子组件中,如果有状态需要进行共享,常见的方法是,将状态提升到最近的公共祖先节点上,但是这样带来的问题是,如果不进行额外的处理,会造成全局的重新渲染。当然,可以使用 useMemo + immutable 进行优化。另外一种解决方法是使用 Context,使用 Context 如果想解决全局渲染的问题,需要将需要共享的值放在一个独立的 Context 中,这样带来的问题是,我们的组件上,如果有更多的状态需要共享,就需要包裹很多的 Provider,维护起来也比较困难。
Recoil 带来了一个全新的概念 Atom,将状态原子化并分散管理,Atom 是可以进行更新并且被组件所订阅的,只要 Atom 更新,可以精确地对订阅这个 Atom 的组件进行重新渲染。
【Recoil 源码探索】
文章图片
文章图片
派生值
可以根据 Atom,生成新的可被组件订阅的数据,类似 Vue 的计算属性。
文章图片
在这里,更加强大的是,这里的 get 方法里还可以处理异步请求。
3. 核心过程源码 初始化
与 Redux 类似,在使用全局数据时,需要在根组件上包一个组件,在 Recoil 中,需要包裹上组件 RecoilRoot。
文章图片
对于 RecoilRoot,其实是在我们的业务代码上,包了一层 Context,其中提供了获取 store 中的数据,更新 store 中的数据等方法。
文章图片
原子状态定义
Recoil中,将每一个最小的状态单位(即无法从其他状态计算得出)定义为一个 Atom。对于每一个 Atom ,其实返回的是一个对象,包括了 key(这个 key 是从传入的 option 中解构出来的,并且需要全局唯一), 以及提供了 get 和 set 等方法。
文章图片
对于 Atom 而言,必须有一个 set 方法,因为我们定义一个原子数据,肯定需要对其进行修改,否则是没有意义的。而对于 Atom 而言,其 get 方法相对简单,在 key 存在的情况下,直接从一个状态的 Map 中获取即可。
文章图片
派生值定义
对于 selector,也与 Atom 一样,也提供了 get 及其他方法,然而,对于 selector 而言,是可以通过 Atom 得到的,所以 set 方法则不是必须的。
文章图片
对于 get 方法,也与 Atom 有所不同,selector 的 get 多了一个缓存的处理,在没有缓存的情况下,会根据依赖的 Atom,计算得到对应的值,并将这个值进行缓存,在下次获取的时候,如果依赖的值没有变化,则可以从缓存中取值,尽可能地提高性能。
文章图片
在组件中 订阅/更新 共享的值
在 Recoil 中,常见 订阅/更新 的 hooks 有 useRecoilState, useRecoilValue, useSetRecoilState。其中 useRecoilState 包含了其余两个。
文章图片
订阅值
文章图片
useRecoilValue 中主要的调用链如下:
useRecoilValue -> useRecoilValueLoadable -> useRecoilValueLoadable_LEGACY -> subscribeToRecoilValue
文章图片
文章图片
在这里,组件中通过 useRecoilValue(useRecoilState) 使用 Atom 或是 selector,会在组件上增加一个订阅,这个订阅通过 useState 来实现,当对应的 RecoilValue 更新时,对组件进行重新渲染,从而做到最小粒度更新组件。这里十分重要的是,storeState.nodeToComponentSubscriptions.set 的方法,这个方法在全局的 store 上更新了全局唯一的 key 所维护的 subscription map,通过这个 map ,在进行更新值的时候,可以准确找到对应关系,从而触发 forceUpdate 对组件进行重新渲染。
更新值
文章图片
useSetRecoilState 中主要的调用链如下:
useSetRecoilState -> setRecoilValue -> queueOrPerformStateUpdate -> applyActionsToStore -> store.replaceState -> Batcher Effect -> sendEndOfBatchNotifications
这个 store.replaceState 则是在 RecoilRoot 中对 store 进行初始化时设置的方法。
文章图片
其中的 notifyBatcherOfChange.current 则是在
组件中通过 useState 的形式进行 state 的更新,从而触发 useEffect,执行 sendEndOfBatchNotifications文章图片
文章图片
而最终执行的这个 callback,就是上面订阅值的时候,所注册的 subscription,从而达到精确更新对应的组件。
这里需要注意的是,如果组件只需要对共享的状态进行更新,不能使用 useRecoilState,因为使用 useRecoilState 的同时会进行共享状态的订阅,造成不必要的渲染。
4. 总结 从 Recoil 的源码中,可以了解到其通过订阅和更新的分离,确保最小粒度的渲染,以及最小化了数据的单位,利用 selector 产生的派生值进行缓存,提高了计算的效率。
The End
文章图片
推荐阅读
- 读书笔记:博登海默法理学|读书笔记:博登海默法理学 —— 正义的探索(1)
- Android事件传递源码分析
- 探索免费开源服务器tomcat的魅力
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- Vue源码分析—响应式原理(二)
- docker镜像探索----dive工具
- 积极探索|积极探索 绽放生命 ???——心心相印计划:青少年心理工作研讨小组全国大型公益行动第二次活动包头市青山区分校圆满成功