注:本文应用的版本是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 (    <div className="function border">      {name}  {count}  {count1}      <button onClick={() => setCount(pre => pre + 1)}>click</button>    </div>  );}const jsx = (  <div className="box border">    <p>start debugger</p>    <FunctionComponent name="函数组件" />  </div>);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 classexport 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树和dom的比照

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<any, any>,  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;