react hooks在react 16.8版本推出后就广受好评,因为它很好的解决了旧版本react无奈很好的在状态逻辑上复用的问题,同时官网阐明hooks会向后兼容不存在breaking changes,在我的项目中更好的无缝接入。
背景和意义
- 目前我的项目中hooks应用越来越遍及,咱们作为开发者不仅要知其然还要知其所以然
- 让咱们在应用过程中能更快的定位排查问题、性能调优
- 学习和理解优良框架的实现思路
从两个纬度剖析
- 首次渲染
- 更新阶段
DEMO
咱们以最根本的demo开始,其中波及两个根本的hook:
useState和useEffect
一、首次渲染
外围流程
上图就是咱们某一个hook组件在首次渲染时所经验的外围流程,大抵分为三步:
-
组件类型解析(是Function、class类型),而后去执行对应类型的解决办法
- 以后fiberNode(也就是以后组件,react16引入了fiber概念)上进行hook的创立和挂载,将咱们所有的hook api挂载到全局变量上(dispatcher)
- 程序执行以后组件,每遇到一个hook api通过next把它连贯到咱们的以后组件的hook链表上
fiberNode构造
首次渲染实现后的以后fiberNode(组件)中的构造关系能够用下图示意:
源码一览
hook api挂载
首次渲染currentDispatcher为空,先挂载所有hook到以后fiberNode的dispatcher,其实也就是HooksDispatcherOnMountInDEV变量
{
if (currentDispatcher !== null) {
currentDispatcher = HooksDispatcherOnUpdateInDEV;
} else {
currentDispatcher = HooksDispatcherOnMountInDEV;
}
}
看看HooksDispatcherOnMountInDEV外部
发现正式咱们相熟的useState等各种原生hook,他们外部其实是调用的mountXXX办法
HooksDispatcherOnMountInDEV = {
useCallback: function (callback, deps) {
return mountCallback(callback, deps);
},
useEffect: function (create, deps) {
return mountEffect(create, deps);
},
useMemo: function (create, deps) {
return mountMemo(create, deps);
},
useState: function (initialState) {
return mountState(initialState);
}
}
回到咱们的demo,首先是mountState
其实做了三件事:
- 创立以后hook的链表节点,节点的数据结构为上图红框。<font color=’red’>memorizedState</font>是咱们最终返回的初始值;<font color=’red’>queue</font>其实是更新队列,当咱们屡次更新某一状态时须要用queue队列存取和遍历;<font color=’red’>next</font>用来连贯下一个hook
- 将以后hook连贯到以后的fiberNode的hook链表上
- 绑定状态更新办法(dispatchAction),并返回[state,dispatchAction]
持续看demo,到useEffect,外部实际上执行mountEffectImpl办法
function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
// 创立并获取以后hook节点信息
var hook = mountWorkInProgressHook();
hook.memoizedState = pushEffect(HasEffect | hookEffectTag, create, undefined, nextDeps);
}
function mountWorkInProgressHook() {
// 将以后hook连贯到咱们的hook链表中
var hook = {
memoizedState: null,
queue: null,
next: null
};
if (workInProgressHook === null) {
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
function pushEffect(tag, create, destroy, deps) {
var effect = {
tag: tag, // 更新标识
create: create, // 传入的回调,也就是咱们开发时的第一个参数
destroy: destroy, // return 的函数,组件销毁时执行的函数
deps: deps, // 依赖项数组
next: null
};
var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
// 这里做的就是把每个useEffect hook独自以链式构造存到了componentUpdateQueue这个全局变量中
if (componentUpdateQueue === null) {
componentUpdateQueue = createFunctionComponentUpdateQueue();
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
var lastEffect = componentUpdateQueue.lastEffect;
var firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
return effect;
}
综上useEffect外部做了两件事:
- mountWorkInProgressHook办法,就是将以后hook连贯到咱们的fiberNode的hook链表中
- 定义effect对象存储咱们传入的信息,同时将hook存入到<font color=’red’>componentUpdateQueue</font>更新队列(这个队列是用来专门存储useEffect hook的)
至此咱们首次渲染完结,咱们此时fiberNode的hook链式构造为
// 以后fiber节点的外部hook链
currentFiber:{
...
memoizedState:{
memoizedState:xxx,
...
next:{
memoizedState:xxx,
...
next:{
memoizedState:xxx,
...
next:hook4
}
}
}
}
更直观一些看的话如下图所示
发表回复