共计 2173 个字符,预计需要花费 6 分钟才能阅读完成。
Recoil 源码摸索
作者:伍然
明天,由伍然带来《Recoil 源码摸索》的分享,让咱们一起学起来,造起来!
1. 简介
Recoil 是 facebook 推出的一个状态解决方案,次要的特点有:1. 高性能的渲染,2. 通过状态生成派生值。
2. 特点
高性能渲染
在 List 和 Canvas 中的第二个子组件中,如果有状态须要进行共享,常见的办法是,将状态晋升到最近的公共先人节点上,然而这样带来的问题是,如果不进行额定的解决,会造成全局的从新渲染。当然,能够应用 useMemo + immutable 进行优化。另外一种解决办法是应用 Context,应用 Context 如果想解决全局渲染的问题,须要将须要共享的值放在一个独立的 Context 中,这样带来的问题是,咱们的组件上,如果有更多的状态须要共享,就须要包裹很多的 Provider,保护起来也比拟艰难。
Recoil 带来了一个全新的概念 Atom,将状态原子化并扩散治理,Atom 是能够进行更新并且被组件所订阅的,只有 Atom 更新,能够准确地对订阅这个 Atom 的组件进行从新渲染。
派生值
能够依据 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 则是在 <Batcher>
组件中通过 useState 的模式进行 state 的更新,从而触发 useEffect,执行 sendEndOfBatchNotifications
而最终执行的这个 callback,就是下面订阅值的时候,所注册的 subscription,从而达到准确更新对应的组件。
这里须要留神的是,如果组件只须要对共享的状态进行更新,不能应用 useRecoilState,因为应用 useRecoilState 的同时会进行共享状态的订阅,造成不必要的渲染。
4. 总结
从 Recoil 的源码中,能够理解到其通过订阅和更新的拆散,确保最小粒度的渲染,以及最小化了数据的单位,利用 selector 产生的派生值进行缓存,进步了计算的效率。
The End