关于前端:react17003读源码

注:本文应用的版本是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 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的比照

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;

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理