React源码-文件结构和入口代码分析

388 阅读3分钟

1.React仓库文件结构

根目录

- fixtures 一些小型的测试项目
- packages 包含React库中所有源码,在子目录src中
- script 包含eslint,jest,prettier等脚本

packages/src

react核心   React暴露给全局的代码,如createElement,hooks等
react-dom  是浏览器Render层的核心代码,包含SSR,createRoot,render等方法
react-native-renderer
react-reconciler 协调器的实现代码,可以用它实现自己的Renderer
scheduler  调度器的实现代码
shared 暴露公共的方法和全局变量

2.入口代码分析;

从react-dom中引入createRoot方法,创建根节点。需要在index.html中放置一个id=‘app’的容器。

import { createRoot } from "react-dom/client";
const FunctionComponent=()=>{
    return <div>组件</div>
}
let element = <FunctionComponent />
const root = createRoot(document.getElementById("root"));
root.render(element);

2.1 createRoot方法

在createRoot方法中创建container并且监听事件,createContainer,updateContainer方法来自react-reconciler/src/ReactFiberReconciler; createContainer这里创建fiber根节点,记录根节点的数据,并且初始化更新队列。

function ReactDOMRoot(internalRoot) {
  this._internalRoot = internalRoot;
}
ReactDOMRoot.prototype.render = function (children) {
  const root = this._internalRoot;
  root.containerInfo.innerHTML = '';
  updateContainer(children, root);
}
export function createRoot(container) {// div#root
  const root = createContainer(container);
  //listenToAllSupportedEvents 监听根容器,也就是div#root只监听一次,  
  // 遍历所有的原生的事件比如click,进行监听
  listenToAllSupportedEvents(container);

  return new ReactDOMRoot(root);
}

2.2 FiberNode的属性:

function FiberNode(tag, pendingProps, key) {
  this.tag = tag;
  this.key = key;
  this.type = null; //fiber类型,来自于 虚拟DOM节点的type  span div p
  //每个虚拟DOM=>Fiber节点=>真实DOM
  this.stateNode = null; //此fiber对应的真实DOM节点  h1=>真实的h1DOM

  this.return = null; //指向父节点
  this.child = null; //指向第一个子节点
  this.sibling = null; //指向弟弟

  //fiber通过虚拟DOM节点创建,虚拟DOM会提供pendingProps用来创建fiber节点的属性
  this.pendingProps = pendingProps; //等待生效的属性
  this.memoizedProps = null; //已经生效的属性

  //每个fiber还会有自己的状态,每一种fiber 状态存的类型是不一样的
  //类组件对应的fiber 存的就是类的实例的状态,HostRoot存的就是要渲染的元素
  this.memoizedState = null;
  //每个fiber身上可能还有更新队列
  this.updateQueue = null;
  //副作用的标识,表示要针对此fiber节点进行何种操作
  this.flags = NoFlags; //自己的副作用
  //子节点对应的副使用标识
  this.subtreeFlags = NoFlags;

  //`currentFiber`和`workInProgressFiber`,通过`alternate`来实现指向
  this.alternate = null;
  this.index = 0;
  this.deletions = null;
  this.lanes = NoLanes;
  this.childLanes = NoLanes;
  this.ref = null;
}

createContainer,FiberRootNode中记录优先级,等待被更新的lane,每个lane的过期时间,还有过期的赛道。 updateContainer中创建更新,获取当前的根fiber,把此更新对象添加到current这个根Fiber的更新队列上,返回根节点

function FiberRootNode(containerInfo) {
  this.containerInfo = containerInfo;//div#root
  //表示此根上有哪些赛道等待被处理
  this.pendingLanes = NoLanes;
  this.callbackNode = null;
  this.callbackPriority = NoLane;
  //过期时间 存放每个赛道过期时间
  this.expirationTimes = createLaneMap(NoTimestamp);
  //过期的赛道
  this.expiredLanes = NoLanes;
}

export function createFiberRoot(containerInfo) {
  const root = new FiberRootNode(containerInfo);
  //HostRoot指的就是根节点div#root
  const uninitializedFiber = createHostRootFiber();
  //根容器的current指向当前的根fiber
  root.current = uninitializedFiber;
  //根fiber的stateNode,也就是真实DOM节点指向FiberRootNode
  uninitializedFiber.stateNode = root;
    //创建一个新的更新队列,pending是一个循环链表
  initialUpdateQueue(uninitializedFiber);
  return root;
}
export function updateContainer(element, container) {
  //获取当前的根fiber
  const current = container.current;
  const eventTime = requestEventTime();
  //请求一个更新车道 16,获取更新的优先级
  const lane = requestUpdateLane(current);
  //创建更新
  const update = createUpdate(lane);
  //要更新的虚拟DOM
  update.payload = { element }; //h1
  //把此更新对象添加到current这个根Fiber的更新队列上,返回根节点
  const root = enqueueUpdate(current, update, lane);
  //当我们向一个fiber上添加一个更新的时候,要把此更新的赛道合并到此fiber的赛道上
  //fiber.lanes = mergeLanes(fiber.lanes, lane);
  //这里进行优先级调度,  //如果新的优先级和老的优先级一样,则可以进行批量更新

  scheduleUpdateOnFiber(root, current, lane, eventTime);
}

从createRoot分析react初始创建根节点做的一些事情,这里涉及到更新优先级,后面我们一块看下优先级的设计,不同事件对应的优先级和lane模型怎么和scheduler中的优先级对接的。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4天,点击查看活动详情