跳到主要内容

React源码:Fiber结构

· 阅读需 8 分钟

react15的协调【reconcile】过程是不能打断的,那么在进行大量节点更新的时候,就会造成卡顿,因为浏览器所有的时间都用来执行js,而js的执行是单线程的,所以在协调过程中就会卡顿。

react16之后,添加schedule过程,也就是调度过程,给每一个工作单元一定的时间,在这个时间内没有执行完成的,就暂时跳出来。那么异步中断的更新需要一定的数据结构来存储工作单元的信息,这就是Fiber。

Fiber的作用:

  1. 作为工作单元,存储了节点信息以及节点的优先级,这些节点通过指针形式构成了Fiber树;
  2. 增量渲染更新,通过jsx对象和currentFiber对比,找出差异,并且应用到真实DOM上去
  3. 根据节点的优先级暂停、继续、排序优先级:因为Fiber上存有优先级,那么就可以通过不同节点的优先级的对比,来完成任务的暂停、继续、排列优先级,为上层实现批量更新、Suspense提供基础;
  4. 保存状态:Fiber上有状态和更新信息,就可以实现函数组件的状态更新,这就是hooks。

Fiber结构

function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 实例
this.tag = tag; // 标记不同的组件类型
this.key = key; // reactElement的key,也就是key属性
this.elementType = null; // 元素类型,是createElement函数的第一个参数
this.type = null; // 异步组件resolve返回的内容,是function或者class
this.stateNode = null; // 真实dom节点

// Fiber树的结构
this.return = null; // 指向父节点,处理完该节点后,向上返回
this.child = null; // 指向child
this.sibling = null; // 指向兄弟节点
this.index = 0;

this.ref = null; // ref属性

this.pendingProps = pendingProps; // 新变动带来新的props
this.memoizedProps = null; // 上次渲染后的props
this.updateQueue = null; // 更新队列存放组件产生的update
this.memoizedState = null; // 上次渲染的state
this.dependencies = null; //

this.mode = mode; //

// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;

//优先级
this.lanes = NoLanes;
this.childLanes = NoLanes;
// current和workInProgress的指针
this.alternate = null;

// 启动分析器时间
if (enableProfilerTimer) {
this.actualDuration = Number.NaN; // 实际持续时间
this.actualStartTime = Number.NaN; // 实际开始时间
this.selfBaseDuration = Number.NaN; //
this.treeBaseDuration = Number.NaN;

this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}

if (__DEV__) {
// This isn't directly used but is handy for debugging internals:

this._debugSource = null;
this._debugOwner = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}

这就是Fiber的结构。

创建FiberRoot和rootFiber

Fiber保存的是dom节点信息,当前dom对应的Fiber树是current Fiber,正在构建的Fiber树是workInProgress Fiber,它是在createWorkInProgress中完成。

首次渲染的时候,会创建fiberRootNode和rootFiber,fiberRootNode是应用的根节点,rootFiber是,之后根据jsx对象创建Fiber节点,Fiber节点连接形成Fiber树。

先创建fiberRoot,在ReactFiberRoot.old.js中找到createFiberRoot函数,这个函数就是创建FiberRoot,源码如下:

function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
initialChildren: ReactNodeList,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: null | ((error: mixed) => void),
transitionCallbacks: null | TransitionTracingCallbacks,
): FiberRoot {
console.log("创建FiberRootNode节点")
const root: FiberRoot = (new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError,
): any);
if (enableSuspenseCallback) {
root.hydrationCallbacks = hydrationCallbacks;
}

if (enableTransitionTracing) {
root.transitionCallbacks = transitionCallbacks;
}

const uninitializedFiber = createHostRootFiber(
tag,
isStrictMode,
concurrentUpdatesByDefaultOverride,
);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;

if (enableCache) {
const initialCache = createCache();
retainCache(initialCache);

root.pooledCache = initialCache;
retainCache(initialCache);
const initialState: RootState = {
element: initialChildren,
isDehydrated: hydrate,
cache: initialCache,
transitions: null,
pendingSuspenseBoundaries: null,
};
uninitializedFiber.memoizedState = initialState;
} else {
const initialState: RootState = {
element: initialChildren,
isDehydrated: hydrate,
cache: (null: any), // not enabled yet
transitions: null,
pendingSuspenseBoundaries: null,
};
uninitializedFiber.memoizedState = initialState;
}

initializeUpdateQueue(uninitializedFiber);

return root;
}

里面的createHostRootFiber方法,是创建rootFiber的。在react的入口文件中,我们调用的是ReactDOM的createRoot方法。这是react应用默认的入口代码:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();

在createRoot方法中,是通过createContainer方法来创建root的,源码如下:

function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// 其他代码省略
const root = createContainer(
container,
ConcurrentRoot,
null,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
);
// 其他代码省略
return new ReactDOMRoot(root);
}

再来看看createContainer的源码:

export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: (error: mixed) => void,
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
const initialChildren = null;
return createFiberRoot(
containerInfo,
tag,
hydrate,
initialChildren,
hydrationCallbacks,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
);
}

这就Fiber最开始创建流程。

创建workInProgress Fiber

workInProgress Fiber,是组件发生更新时,生成新的jsx和current Fiber树进行对比【diff算法】后,生成workInProgress Fiber,然后FiberRoot的current指向workInProgress Fiber,那么workInProgress Fiber就变成的current Fiber,从而完成更新。完整源码如下:

export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
let workInProgress = current.alternate;
if (workInProgress === null) { // 是否是首次渲染还是更新阶段
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;

if (__DEV__) {
// DEV-only fields

workInProgress._debugSource = current._debugSource;
workInProgress._debugOwner = current._debugOwner;
workInProgress._debugHookTypes = current._debugHookTypes;
}

workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps; // 复用属性
// Needed because Blocks store data on type.
workInProgress.type = current.type;

workInProgress.flags = NoFlags;

// The effects are no longer valid.
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;

if (enableProfilerTimer) {
workInProgress.actualDuration = 0;
workInProgress.actualStartTime = -1;
}
}

workInProgress.flags = current.flags & StaticMask;
workInProgress.childLanes = current.childLanes;
workInProgress.lanes = current.lanes;

workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;

const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
};

// These will be overridden during the parent's reconciliation
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;

if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
workInProgress.treeBaseDuration = current.treeBaseDuration;
}

if (__DEV__) {
workInProgress._debugNeedsRemount = current._debugNeedsRemount;
switch (workInProgress.tag) {
case IndeterminateComponent:
case FunctionComponent:
case SimpleMemoComponent:
workInProgress.type = resolveFunctionForHotReloading(current.type);
break;
case ClassComponent:
workInProgress.type = resolveClassForHotReloading(current.type);
break;
case ForwardRef:
workInProgress.type = resolveForwardRefForHotReloading(current.type);
break;
default:
break;
}
}

return workInProgress;
}

这就是Fiber数据结构完整流程