阅读前须知
流程图的内容90%来自于React技术揭秘一书,对其内容进行整理,方便正在学习源码的同学们能够系统性把每个关键知识点给串联起来,下面JPG图片比较模糊,图片宽高比较大,用手机打开pdf,观看起来也不方便,建议用电脑跳转下面各个pdf地址进行查看,带着问题来阅读。
React Hook
1、一个极简的useState Hook的实现(强烈推荐,下面把这部分的代码贴出来了,对理解hook有帮助)
2、Hook 的数据结构(类似updateQueue)
3、mount/update的调起方法会以不同dispatcher区分
4、useState与useReducer原理概览(对比上面极简useState hook的实现)
5、usEffect(通过前几个流程图所讲flushPassiveEffects方法进行切入理解)概览
6、useRef原理概览?ref的工作流程,render阶段做了什么?commit阶段做了什么?
7、useMemo、useCallback原理概览,以及mount、update时两者的区别
极简useState Hook的实现
// 首次render时是mount
let isMount = true;
// 通过workInProgressHook变量指向当前正在工作的hook
let workInProgressHook;
// App组件对应的fiber对象
const fiber = {
// 保存该FunctionComponent对应的Hooks链表
memoizedState: null,
// 指向App函数
stateNode: App
};
// APP组件
function App() {
const [num, updateNum] = useState(0);
console.log(`${isMount ? 'mount' : 'update'} num: `, num);
return {
click() {
updateNum(num => num + 1);
}
}
}
// 模拟React开始调度更新
function schedule() {
// 更新前将workInProgressHook重置为fiber保存的第一个Hook
// 通过workInProgressHook变量指向当前正在工作的hook
workInProgressHook = fiber.memoizedState;
// 触发组件render
fiber.stateNode();
// 组件首次render为mount,以后再触发的更新为update
isMount = false;
}
// 更新时底层驱动函数
function dispatchAction(queue, action) {
// 创建update
const update = {
action, // 更新执行的函数
next: null // 与同一个Hook的其他更新形成链表
}
// 环状单向链表操作
// 产生的update保存在useState对应的hook.queue
if (queue.pending === null) {
update.next = update;
} else {
update.next = queue.pending.next;
queue.pending.next = update;
}
queue.pending = update;
// 模拟React开始调度更新
schedule();
}
function useState(initialState) {
let hook;
if (isMount) {
// mount时需要生成hook对象
// 保存update的queue,即上文介绍的queue
hook = {
queue: {
pending: null
},
// 保存hook对应的state
memoizedState: initialState,
// 与下一个Hook连接形成单向无环链表
next: null
}
// 将hook插入fiber.memoizedState链表末尾
if (!fiber.memoizedState) {
fiber.memoizedState = hook;
} else {
workInProgressHook.next = hook;
}
// 移动workInProgressHook指针
workInProgressHook = hook;
} else {
// update时从workInProgressHook中取出该useState对应的hook
hook = workInProgressHook;
// 在组件render时,每当遇到下一个useState,我们移动workInProgressHook的指针。
// 这样,只要每次组件render时useState的调用顺序及数量保持一致,那么始终可以通过workInProgressHook找到当前useState对应的hook对象。
workInProgressHook = workInProgressHook.next;
}
let baseState = hook.memoizedState;
if (hook.queue.pending) {
// 获取update环状单向链表中第一个update
let firstUpdate = hook.queue.pending.next;
do {
// 执行update action
const action = firstUpdate.action;
baseState = action(baseState);
firstUpdate = firstUpdate.next;
// 最后一个update执行完后跳出循环
} while (firstUpdate !== hook.queue.pending.next)
// 清空queue.pending
hook.queue.pending = null;
}
// 将update action执行完后的state作为memoizedState
hook.memoizedState = baseState;
return [baseState, dispatchAction.bind(null, hook.queue)];
}
后续会把后续会把所有流程图进行分享,以及将.pos源文件进行分享。