共计 2938 个字符,预计需要花费 8 分钟才能阅读完成。
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
}
}
}
}
更直观一些看的话如下图所示
正文完