读react源码的那些年那些事

194 阅读2分钟

准备工作

我是如何阅读源码的

react 下载地址

react-dom 下载地址

如果是使用vscode的大佬,建议下载插件bookmarks 、better Comments。

通过 Bookmarks 去添加标签做快速跳转。

配置可以参照文章中的,只是下载的文件还是走上面链接的去下载

阅读源码切记要盯紧目标,放弃分支

从入口开始

最简单的例子:

ReactDOM.render(
    React.createElement(ForRender,{val:100}),
    document.getElementById('root')
)

源码部分:

第一步 render 方法:
render: function (element, container, callback) {
    // element = React.createElement(ForRender,{val:100})
    // 执行出来的话是  
    var element = {
        // This tag allows us to uniquely identify this as a React Element
        $$typeof: REACT_ELEMENT_TYPE,

        // Built-in properties that belong on the element
        type: type,
        key: key,
        ref: ref,
        props: props,

        // Record the component responsible for creating this element.
        _owner: owner
      };
      
    // container = document.getElementById('root')
    return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
  },
第二步 legacyRenderSubtreeIntoContainer方法:
// legacyRenderSubtreeIntoContainer 渲染子节点到容器内
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
     var root = container._reactRootContainer;
     // root 绝对是undefined的,因为默认的div是不可能有_reactRootContainer的
     if(!root){
         // 基于dom节点去创建根节点
         root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
        // 进入此函数时,root节点已经初始化完毕
        // Initial mount should not be batched.
      
        unbatchedUpdates(function () {
        //  parentComponent 在render 传过来时一定为空
          if (parentComponent != null) {
            root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
          } else {
            root.render(children, callback);
          }
        });
     }
     
     
}
第三步 legacyCreateRootFromDOMContainer 方法:
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
    // 前面唯一需要注意的就是这句
    container.removeChild(rootSibling);
    删除root根节点的所有子节点
    ...
  var isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}
第四步 ReactRoot 方法:
// ReactRoot 是一个构造函数,所以晚点在下面去分析原型挂载的render方法
function ReactRoot(container, isConcurrent, hydrate) {
  // 传入的参数是: div false false 
  
  var root = createContainer(container, isConcurrent, hydrate);
  // createContainer 创建一个容器
 
  this._internalRoot = root;
}
第五步 createContainer 方法:
function createContainer(containerInfo, isConcurrent, hydrate) {
   // 创建一个fiberRoot
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}
第六步 createFiberRoot 方法:
// 从方法来看就是创建一个root对象,里面有一堆属性。
function createFiberRoot(containerInfo, isConcurrent, hydrate) {
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  var uninitializedFiber = createHostRootFiber(isConcurrent);

  var root = void 0;
  if (enableSchedulerTracing) {
    root = {
      current: uninitializedFiber,
      containerInfo: containerInfo,
      pendingChildren: null,

      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,

      pingCache: null,

      didError: false,

      pendingCommitExpirationTime: NoWork,
      finishedWork: null,
      timeoutHandle: noTimeout,
      context: null,
      pendingContext: null,
      hydrate: hydrate,
      nextExpirationTimeToWorkOn: NoWork,
      expirationTime: NoWork,
      firstBatch: null,
      nextScheduledRoot: null,

      interactionThreadID: unstable_getThreadID(),
      memoizedInteractions: new Set(),
      pendingInteractionMap: new Map()
    };
  } else {
    root = {
      current: uninitializedFiber,
      containerInfo: containerInfo,
      pendingChildren: null,

      pingCache: null,

      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,

      didError: false,

      pendingCommitExpirationTime: NoWork,
      finishedWork: null,
      timeoutHandle: noTimeout,
      context: null,
      pendingContext: null,
      hydrate: hydrate,
      nextExpirationTimeToWorkOn: NoWork,
      expirationTime: NoWork,
      firstBatch: null,
      nextScheduledRoot: null
    };
  }

  uninitializedFiber.stateNode = root;
  return root;
}

fiberRoot 留到后面再分析里面的每一项是啥东西。 感兴趣可以看下fiberRoot数据结构

第七步 ReactRoot.prototype.render
ReactRoot.prototype.render = function (children, callback) {
  var root = this._internalRoot;
   ....
   
  updateContainer(children, root, null, work._onCommit);
  return work;
};

....后续补充,

expirationTime理解

expirationTime 到期时间,用于描述任务队列的到期时间。expirationTime的计算有点难理解,

首先第一个理解什么是 unstable_now ,通过源码定位到

var localDate = Date;
if (hasNativePerformanceNow) {
  var Performance = performance;
  getCurrentTime = function() {
    return Performance.now();
  };
} else {
  getCurrentTime = function() {
    return localDate.now();
  };
}

所以现在就需要来理解一下什么是performance.now()了。performance.now()简单来说就是从网页打开到你打印这个值的毫秒时间。不是从1970年开始的时间戳。

未完待续...