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天,点击查看活动详情