本文章中为了精简会删除一些不必要的属性和注释,react源码在这里,有需要的自己去拉一下:React源码
一.fiber
在常见面试题(一)中曾说过Fiber可以看做是一种数据结构, Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行,即可以中断与恢复。今天就找来了关于Fiber的源码,废话少说先上源码
// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
export type Fiber = {
// Tag identifying the type of fiber.
tag: WorkTag,
// Unique identifier of this child.
key: null | string,
// The value of element.type which is used to preserve the identity during
// reconciliation of this child.
elementType: any,
// The resolved function/class/ associated with this fiber.
type: any,
// The local state associated with this fiber.
stateNode: any,
// The Fiber to return to after finishing processing this one.
// This is effectively the parent, but there can be multiple parents (two)
// so this is only the parent of the thing we're currently processing.
// It is conceptually the same as the return address of a stack frame.
return: Fiber | null,
// Singly Linked List Tree Structure.
child: Fiber | null,
sibling: Fiber | null,
index: number,
// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
ref:
| null
| (((handle: mixed) => void) & {_stringRef: ?string, ...})
| RefObject,
refCleanup: null | (() => void),
// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any, // This type will be more specific once we overload the tag.
memoizedProps: any, // The props used to create the output.
// A queue of state updates and callbacks.
updateQueue: mixed,
// The state used to create the output
memoizedState: any,
// Dependencies (contexts, events) for this fiber, if it has any
dependencies: Dependencies | null,
// Effect
flags: Flags,
subtreeFlags: Flags,
deletions: Array<Fiber> | null,
lanes: Lanes,
childLanes: Lanes,
// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
alternate: Fiber | null,
};
在开头中,也说了"fiber是一个需要完成或者已经完成的组件,它可以存在多个"。这也印证了开头所说的:
Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分
别看源码中fiber的属性有这么多,其实我们需要主要了解的就以下几个:tag
用于标识 fiber 的类型,例如函数组件、类组件等。stateNode
是Fiber关联的本地状态节点通常指向组件实例或指向实际的 DOM 节点。key
用于在同级元素中唯一标识一个 fiber,以帮助 React 在更新时识别和复用元素。 return
返回的是完成处理后的纤程,其指向父级 fiber,相当于调用栈中的返回地址。child
和 sibling
分别指向子 fiber 和兄弟 fiber,从此就可以形成一个单链表树结构。pendingProps
和 memoizedProps
分别表示待处理的属性和已处理的属性。updateQueue
包含状态更新和回调的队列。
了解完这几个属性再去深入了解fiber,就会发现容易理解很多
二.Dispatcher
hooks相信大家都特别了解了,面对这么复杂的源码,我肯定第一时间先去挑个软柿子捏一捏,去找一下useState,然后当我去react-main/packages/react/src/ReactHooks.js
找到useState的时候我有点懵。怎么回事?怎么返回的是dispactcher的useState?这个dispatcher又是什么来的?
//软柿子//
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
在然后顺着一步步找上去,找到了Dispatcher
的源码,Dispatcher 是 React 内部用于管理 Hooks 的一个核心接口。它的主要作用是提供一组方法,用于管理和执行组件中的各种 Hook。这些方法允许 React 在组件的生命周期中管理状态、上下文、引用、副作用等。我们先来看一下源码:
//react-main/packages/react-reconciler/src/ReactInternalTypes.js
export type Dispatcher = {
use: <T>(Usable<T>) => T,
readContext<T>(context: ReactContext<T>): T,
useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>],
useReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: (I) => S,
): [S, Dispatch<A>],
useContext<T>(context: ReactContext<T>): T,
useRef<T>(initialValue: T): {current: T},
useEffect(
create: (() => (() => void) | void) | (() => {...} | void | null),
createDeps: Array<mixed> | void | null,
update?: ((resource: {...} | void | null) => void) | void,
updateDeps?: Array<mixed> | void | null,
destroy?: ((resource: {...} | void | null) => void) | void,
): void,
// TODO: Non-nullable once `enableUseEffectEventHook` is on everywhere.
useEffectEvent?: <Args, F: (...Array<Args>) => mixed>(callback: F) => F,
useInsertionEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void,
useLayoutEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void,
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T,
useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T,
useImperativeHandle<T>(
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
create: () => T,
deps: Array<mixed> | void | null,
): void,
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void,
useDeferredValue<T>(value: T, initialValue?: T): T,
useTransition(): [
boolean,
(callback: () => void, options?: StartTransitionOptions) => void,
],
useSyncExternalStore<T>(
subscribe: (() => void) => () => void,
getSnapshot: () => T,
getServerSnapshot?: () => T,
): T,
useId(): string,
useCacheRefresh: () => <T>(?() => T, ?T) => void,
useMemoCache: (size: number) => Array<any>,
useHostTransitionStatus: () => TransitionStatus,
useOptimistic: <S, A>(
passthrough: S,
reducer: ?(S, A) => S,
) => [S, (A) => void],
useFormState: <S, P>(
action: (Awaited<S>, P) => S,
initialState: Awaited<S>,
permalink?: string,
) => [Awaited<S>, (P) => void, boolean],
useActionState: <S, P>(
action: (Awaited<S>, P) => S,
initialState: Awaited<S>,
permalink?: string,
) => [Awaited<S>, (P) => void, boolean],
// TODO: Non-nullable once `enableSwipeTransition` is on everywhere.
useSwipeTransition?: <T>(
previous: T,
current: T,
next: T,
) => [T, StartGesture],
};
是不是觉得有点熟悉,为什么我们常见的hooks都在这里面?这就要说说Dispatcher是如何管理和执行其中的hooks的:
1.首先Dispatcher是动态分配的,其具体实现在不同的渲染阶段会有所不同(例如在组件挂载时,Dispatcher 会被设置为 HooksDispatcherOnMount,在更新时会被设置为 HooksDispatcherOnUpdate),可以对比图中两个Dispatcher的不同,感兴趣的小伙伴可以去react-main\packages\react-reconciler\src\ReactFiberHooks.js
这个文件下看一下
2.在组件的渲染过程中,当调用 Hook(如 useState、useEffect 等)时,实际上是通过当前的 Dispatcher 实例来调用相应的方法。这些方法会根据当前的渲染阶段和组件状态(用于Dispathcher动态分配存在差异),执行相应的逻辑。
3.每个 Hook 的调用都会在内部维护一个链表结构,用于存储 Hook 的状态和相关信息。Dispatcher 的实现会管理这些链表,确保在每次渲染时正确地更新和复用 Hook 的状态。
4.对于副作用(如 useEffect),Dispatcher 会在渲染完成后调度这些副作用的执行。副作用的清理函数也会在适当的时候被调用,通常是在组件卸载或依赖项变化时。
5.Dispatcher 通过记忆化技术(如 useMemo 和 useCallback)来减少不必要的计算和渲染。通过管理 Hook 的依赖项,Dispatcher 可以避免在依赖项未变化时重复执行相同的逻辑。