作者:知鸟 聂凡坤
这篇文章是从源代码的角度剖析react hooks,在开始之前,对于react hooks的重要性以及为什么需要用函数组件可以阅读:
一、阅读源码的误区
首先我们需要找到源代码,不少萌新小伙伴可能会从node_modules
中的react模块包中去找hooks的源代码,如下图
你所看到的只有这些hooks的定义,而其值的实现方式都是以var dispatcher = resolveDispatcher()
;再由这个dispatcher
去调用对应的不同的事件,对于resolveDispatcher()
方法的实现也会看得一头雾水,ReactCurrentDispatcher
在这个源代码中啥都没有。所以从node_modules
中react模块包解读hooks是行不通的。
二、从哪里开始阅读源码
正确的方式是从github react 的仓库下载源代码仓库能进行解读。下载好了之后我们可以看到packages有32个模块包,接下来的讲解都在packages路径下,跟hooks强相关的react
,react-reconciler
两个模块包。
上面的图可以看到这是真正意义上的专门的定义react hooks
的本源,再继续寻找ReactCurrentDispatcher
我们可以看到是引入了一个类型Dispatcher
(派发者),派发者类型定义如图:
如上文件只暴露出的type Dispatcher
里只提供了常用的如useState
,useEffect
等方法的类型定义,怎样才能发现更多有用的信息呢?
三、如何快速定位方法的源码实现
源代码研究不下去的时候应该换个角度,既然是只是定义类型,那一定有其他地方也会调用该类型从而获取hooks的更深层的实现。所以这里用了一个取巧的方式:在react-reconciler中搜索Dispatcher
可以看到两个文件引用了ReactInternalTypes
中定义的Dispatcher
打开ReactFiberHooks.old.js
之后就可以发现新大陆。
这里可以看到引入类Dispatcher
的实现有三个对象HooksDispatcherOnMount
(hooks派发在组件加载时),HooksDispatcherOnUpdate
(hooks派发在组件更新时),HooksDispatcherOnRerender
(hooks派发在组件渲染。这三个对象都是集中在renderWithHooks
的函数之中(图不贴,请读者自行寻找)。
本着追本溯源的方式我们找寻renderWithHooks
的引用文件。在ReactFiberBeginWork.old.js
中都使用在了updateFunctionComponent
函数中,而这个函数是调用在了case FunctionComponent
中,而代码下方的case ClassComponent
所调用的方法的的updateClassComponent
,所以,如下
由于找到代码是采用的取巧的方式,再回过头总结下hooks的实现为一个词--分片任务调度。对于分片这个词感到陌生的小伙伴可以拜读下这篇文章
这篇文章中有提到两个阶段,Reconcile
阶段和Commit
阶段从这两个阶段分别对应ReactFiberBeginWork.old.js
和ReactFiberCommitWork.old.js
(现在文件中old
和new
代码都一样,可能facebook
官方要做优化重构所以复制一份出来用old,new作为区分)。
四、在reconcile
阶段调用核心方法renderWithHooks
在reconcile
阶段当解析到为函数组件时会调用核心方法renderWithHooks
:
此方法会根据当前的分片任务中的current
属性是否有任务去判断执行HooksDispatcherOnMount
还是HooksDispatcherOnUpdate
,在渲染阶段react
调度器会适时得执行HooksDispatcherOnRerender
五、在commit
阶段对hook
执行队列解除
在commit
阶段当解析到为函数组件时会对hook
执行队列解除:
对于commitHookEffectListUnmount
字面意思为解除提交过的hooks执行队列,将当前的一个个执行的hooksEffect
置空或者销毁。
文章篇幅有限,蜻蜓点水,只对hooks怎么通过react的调度机制运行以及如何找寻hooks的源码进行解读,整体运行的机制还需要各位看官自行再看源代码解读。
下一个篇幅将介绍hooks的那些方法源码的实现。