ReactDom.render 源码阅读

2019-07-01  本文已影响0人  我家小八真可爱

React16 源码简介

React16 重写了核心算法 reconciler。因为 JavaScript 引擎线程和 UI 渲染线程虽然不是一个线程,但二者是互斥的(因为JS运行结果会影响到UI线程的结果),当浏览器在处理 JavaScript 时,页面就会停止渲染,一旦 JavaScript 执行占用的时间过长,留给 UI 渲染的时间就会缩短,从而造成页面每秒渲染的帧数过低,导致页面产生明显的卡顿感。

React 源码中 package 下的几个关键模块:

ReactDom.render

前言

ReactDom.render(
    element: React$Element<any>,
    container: DOMContainer,
    callback: ?Function,
  )

ReactDom.render 的第一个参数为 ReactElement,那么 element、component 和 instance 的关系是什么呢?

element:一个可以描述 DOM 节点或者 component 实例的对象,可以通过 React.createElement() 或者 JSX 语法创建。
component:可以通过 class 或者 function 声明。
instance:只有类组件才有实例,React 会自动创建实例。

component 👇

class App extends React.Component {
  render() {
    <div>hello!</div>
  }
}

element & instance 👇

<App />

dom element 👇

<div>hello!</div>

源码阅读

reactdom.render.png

1.入口

// packages/react-dom/src/client/ReactDOM.js
render(
    element: React$Element<any>, // 要渲染的元素
    container: DOMContainer, // 根节点
    callback: ?Function, // 渲染完执行的回调
  ) {
    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  }

2.创建 root,然后调用 root.render,最终返回 DOM 节点信息

// packages/react-dom/src/client/ReactDOM.js
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, // null
  children: ReactNodeList, //element
  container: DOMContainer, //container
  forceHydrate: boolean, //false
  callback: ?Function, //callback
) {
  let root: Root = (container._reactRootContainer: any);
  // 首次渲染,root 不存在
  if (!root) {
    // Initial mount
    // 创建 root
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate, // 在 render 中是 false,hydrate 中为 true,决定是否复用 DOM 节点
    );
    if (typeof callback === 'function') {
     // 执行回调
    }
    // Initial mount should not be batched.
    // 不使用 batchedUpdates,因为首次渲染需要尽快完成
    unbatchedUpdates(() => {
      if (parentComponent != null) {
        // ...
      } else {
        // render 中 parentComponent = null
        root.render(children, callback);
      }
    });
  } else {
    // root 存在的情况
  }
  return getPublicRootInstance(root._internalRoot);
}

2.1 创建 root

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  // ...
  // Legacy roots are not async by default.
  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}

function ReactRoot(
  container: DOMContainer,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}

function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  // 最终返回 FiberRoot 对象
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}

2.2 调用 root.render

ReactRoot.prototype.render = function(
  children: ReactNodeList,
  callback: ?() => mixed,
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  if (callback !== null) {
    work.then(callback);
  }
  updateContainer(children, root, null, work._onCommit);
  return work;
};

function updateContainer(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  callback: ?Function,
): ExpirationTime {
  const current = container.current;
  const currentTime = requestCurrentTime();
  const expirationTime = computeExpirationForFiber(currentTime, current);
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}

function updateContainerAtExpirationTime(
  element: ReactNodeList,
  container: OpaqueRoot,
  parentComponent: ?React$Component<any, any>,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {
  const current = container.current;
  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }

  return scheduleRootUpdate(current, element, expirationTime, callback);
}

function scheduleRootUpdate(
  current: Fiber,
  element: ReactNodeList,
  expirationTime: ExpirationTime,
  callback: ?Function,
) {
  // 创建更新
  const update = createUpdate(expirationTime);
  // 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);
  // 开始调度。React16+提出了任务优先级的概念。
  scheduleWork(current, expirationTime);

  return expirationTime;
}

function createUpdate(expirationTime: ExpirationTime): Update<*> {
  return {
    expirationTime: expirationTime,
    tag: UpdateState,
    payload: null,
    callback: null,
    next: null,
    nextEffect: null,
  };
}

2.3 返回真实 DOM

HostComponent:抽象节点,是 ClassComponent 的组成部分。具体的实现取决于 React 运行的平台。在浏览器环境下就代表 DOM 节点。
Fiber:Fiber 是一个对象,表征 reconciliation 阶段所能拆分的最小工作单元,和 React Instance 一一对应。并通过 stateNode 属性管理 Instance 的 local state。

// packages/react-reconciler/src/ReactFiberReconciler.js
function getPublicRootInstance(
  container: OpaqueRoot,
): React$Component<any, any> | PublicInstance | null {
  // container.current 为 container 对应的 Fiber
  const containerFiber = container.current;
  if (!containerFiber.child) {
    return null;
  }
  // 判断 Fiber 子节点的类型
  switch (containerFiber.child.tag) {
    // 真实 DOM 如 div, span 等
    case HostComponent:
      return getPublicInstance(containerFiber.child.stateNode);
    default:
      // stateNode 是跟当前 Fiber 相关联的本地状态(比如浏览器环境就是真实的 DOM 节点)
      return containerFiber.child.stateNode;
  }
}

function getPublicInstance(instance: Instance): * {
  return instance;
}
参考文献

https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html

上一篇下一篇

猜你喜欢

热点阅读