注:本文使用的版本是React-17.0.0.3,开启enableNewReconciler = true,即会使用new.js后缀的文件;
文章专注于hook原理和react的渲染部分,涉及到classComponent的部分一律略过;
mode = concurrent
本文所有代码都是基于以下react代码
import { React } from "../CONST";
import { useEffect, useState } from "../react/packages/react";
function FunctionComponent({name}) {
const [count, setCount] = useState(0);
const [subtraction, setSubTraction] = useState(1);
useEffect(() => {
setSubTraction(10);
}, []);
return (
{name} {count} {count1}
);
}
const jsx = (
start debugger
);
ReactDOM.createRoot(
document.getElementById('root')
).render(jsx);
自顶向下介绍React
关于concurrent模式和Fiber架构等知识请看《React技术揭秘》
fiber的结构
function FiberNode(
tag: WorkTag, // fiberTag
pendingProps: mixed, // 组件参数/属性
key: null | string, // key
mode: TypeOfMode, // 指示是lagecy还是conCurrent模式
) {
// Instance
this.tag = tag; // fiber类型
this.key = key; // 用来做diff算法
this.elementType = null;
this.type = null;
this.stateNode = null; // 该fiber关联的真实dom
// Fiber
this.return = null; // 父fiber
this.child = null; // 第一个孩子fiber
this.sibling = null; // 相邻的第一个兄弟fiber
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps; // 新的组件参数/属性
this.memoizedProps = null; // 已经渲染在页面上的(旧的)组件参数/属性
this.updateQueue = null; // 一个环状链表,存储的是effect副作用形成的update对象
this.memoizedState = null; // 对于functionComponent来说,存储的是hook对象链表
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags; // 本fiber effect形成的副作用
this.subtreeFlags = NoFlags; // 子fiber effect的副作用
this.deletions = null;
this.lanes = NoLanes; //本fiber的update lanes
this.childLanes = NoLanes; // 所有子fiber的update lanes
this.alternate = null;
。。。
}
fibertag用来标示该fiber是哪种组件类型的fiber--25种
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
....
fiber树的生成过程
我们从render开始看fiber树是如何一个一个生成的
export function createRoot(
container: Container,
options?: RootOptions,
): RootType {
return new ReactDOMRoot(container, options);
}
function ReactDOMRoot(container: Container, options: void | RootOptions) {
this._internalRoot = createRootImpl(container, ConcurrentRoot, options);
}
function createRootImpl(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
...
// 生成root
const root = createContainer(
container,
tag,
hydrate,
hydrationCallbacks,
isStrictMode,
concurrentUpdatesByDefaultOverride,
);
...
return root;
}
ReactDOMRoot.prototype.render = ReactDOMLegacyRoot.prototype.render = function(
children: ReactNodeList,
): void {
const root = this._internalRoot;
...
updateContainer(children, root, null, null);
};
ReactDOM.createRoot(document.getElementById('root'))会创建ReactDomRoot对象,该类会调用createRootImpl初始化fiberRoot对象。随后的render函数是挂在类原型上的,会调用updateContainer
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component,
callback: ?Function,
): Lane {
const current = container.current;
const eventTime = requestEventTime();
const lane = requestUpdateLane(current);
const update = createUpdate(eventTime, lane);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
callback = callback === undefined ? null : callback;
if (callback !== null) {
update.callback = callback;
}
enqueueUpdate(current, update, lane);
const root = scheduleUpdateOnFiber(current, lane, eventTime);
return lane;
}
显然该函数是为了给fiberRoot建立一个update,并且update.payload是element即jsx;enqueueUpdate是为了把update挂到fiber.updateQueue;最后调用了scheduleUpdateOnFiber
重点来了,scheduleUpdateOnFiber是调度函数的入口函数,从这里开始进行fiber树的构造以及update的处理;
export function scheduleUpdateOnFiber(
fiber: Fiber,
lane: Lane,
eventTime: number,
): FiberRoot | null {
// 从当前fiber开始向上冒泡直到找到root节点,同时更新所有父节点的childLanes
const root = markUpdateLaneFromFiberToRoot(fiber, lane);
if (root === null) {
return null;
}
// 标记root有一个待更新
markRootUpdated(root, lane, eventTime);
if (lane === SyncLane) {
if (
// Check if we're inside unbatchedUpdates
(executionContext & LegacyUnbatchedContext) !== NoContext &&
// Check if we're not already rendering
(executionContext & (RenderContext | CommitContext)) === NoContext
) {
performSyncWorkOnRoot(root);
} else {
ensureRootIsScheduled(root, eventTime);
}
} else {
// Schedule other updates after in case the callback is sync.
ensureRootIsScheduled(root, eventTime);
}
return root;
}
ensureRootIsScheduled函数是为了向调度堆里push一个回调函数,最后还是会调用performSyncWorkOnFiber/performConcurrentWorkOnFiber;