关于前端:react17003读源码

9次阅读

共计 4451 个字符,预计需要花费 12 分钟才能阅读完成。

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

正文完
 0