react-17.0.0.3读源码

注:本文使用的版本是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树和dom的对比
dom树
fiber树

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;

你可能感兴趣的