React链表结构的核心概念
1. Fiber

- 结构上,是一个对象
- 描述了一个React元素或Dom节点
- 新的协调引擎,主要目的是使Virtual DOM可以进行增量式渲染
export type Fiber = {
// 标记组件类型,如函数组件、类组件、原生便签等
tag: WorkTag,
// 标记节点的唯一性,diff时候判断节点是否能复用的依据之一,其他两个是同一层级、类型相同
key: null | string,
// 保存节点在协调阶段的element.type的值
elementType: any,
// fiber的type
type: any,
// 不同类型的组件,stateNode代表的值不同:
// 原生标签,stateNode是dom节点
// 类组件,stateNode是实例
stateNode: any,
// 父fiber
return: Fiber | null,
// fiber children是单链表结构,这也是React VDOM DIFF算法中单向查找的原因(Vue VDOM DIFF中是双向查找)
// 第一个子fiber
child: Fiber | null,
// 下一个兄弟fiber
sibling: Fiber | null,
// 标记此fiber在当前层级下的位置下标
index: number,
// 节点的ref
ref: null
| (((handle: mixed) => void) & {_stringRef: ?sting, ...}}
| RefObject,
// 还未更新完成组件的props
pendingProps: any,
// 上次组件更新的props
memoizedProps: any,
// update的queue, update里包括了状态值、callback等
// update是个单向循环链表
updateQueue: mixed,
// 类组件中存的是状态值
// 函数组件中存的是第0个hook, hooks也是单链表
memoizedState: any,
// 如果该组件消费了context value, 这里存的就是context对象
dependencies: Dependencies | null
// 如concurrent模式
mode: TypeOfMode,
// 标记fiber的effect, 如Placement(末尾插入appendChild、向前插入insertBefore)、Update(更新)等
flags: Flags,
// 子树的flags
subtreeFlags: Flags,
// 如果有要删除的子fiber, 则存在这个数组中
deletions: Array<Fiber> | null
// 标记节点的lanes
lanes: Lanes,
childLanes: Lanes,
// 上一个fiber
alternate: Fiber | null
}
2. Hook
- 解决类组件难以解决的问题,比如状态复用、组件臃肿等
- 每次函数组件执行的时候,hook都会再次执行;Vue Composition只是首次执行一次
- 结构上,为单链表,hook顺序代表链表的唯一性,所以引用hook不能使用条件、循环等语句
export type Hook = {
// 如果是useState或者useReducer, 这个memoizedState记录的是状态值
// 如果是useEffect或useLayoutEffect, 这个memoizedState记录的是effect
// 如果是useMemo, 这个memoizedState记录的是新值和依赖数组,即[nextValue, nextDeps]
// 如果是useDeferredValue, 这个memoizedState记录的是这个value
memoizedState: any,
baseState: any,
baseQueue: Update<any, any> | null,
queue: any,
// 下一个hook, 单链表结构
next: Hook | null
}
3. Update
- 初次渲染、setState、forceUpdate的更新
- 通过批量更新实现的异步更新
function enqueueUpdate<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
update: Update<S, A>,
lane: Lane
) {
...
}
4. Effect
- useEffect和useLayoutEffect处理effect
函数组件初次渲染
- 记录effect和deps
- pushEffect函数,把effect单向循环链表,存储在fiber.updateQueue.lastEffect上
// effect
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null
):void {
return mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps
)
}
function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.flags = fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps
)
}
函数组件更新阶段
- 相对于初次渲染阶段,更新阶段的不同之处在于多了对deps的对比,如果组件更新前后,依赖项不变,则后续的effect并不会添加到updateQueue中,也就意味着create函数不会执行。
- 并且,初次渲染阶段不会记录destory函数,只需函数组件更新阶段记录到effect中。
- 在记录阶段,effect都是记录到fiber.updateQueue中
5. 总结
- 单向链表fiber、hook
- 单向循环链表update、effect