记录学习过程,如有错误欢迎指出
开始之前(必看)
React version 18.2.0
DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知
我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)
因为使用了深度优先的方式讲解,
耦合比较重,不建议跳着看
入口
//在上一篇文章中我们已经讲完了createRoot干了什么
const root = ReactDOM.createRoot(document.querySelector("#app"));
//现在开始进入render(),非render阶段
root.render(<App />);
render(未完)
export function render(
element: React$Element<any>,// 这里就是传入的jsx就是React$Element,即<App>
container: Container,
callback: ?Function,
) {
.....
// 判断是否是有效的DOM元素
// ok,这里先进入isValidContainerLegacy
if (!isValidContainerLegacy(container)) {
throw new Error('Target container is not a DOM element.');
}
.....
isValidContainerLegacy
export function isValidContainerLegacy(node: any): boolean {
return !!(
node &&
// 即是Element | Doucment | Fragment | COMMENT 就是有效的容器节点
(node.nodeType === ELEMENT_NODE ||
node.nodeType === DOCUMENT_NODE ||
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
(node.nodeType === COMMENT_NODE &&
(node: any).nodeValue === ' react-mount-point-unstable '))
);
}
render(完)
export function render(
element: React$Element<any>,// 这里就是传入的jsx就是React$Element,即<App>
container: Container,
callback: ?Function,
) {
.....
// 渲染子树到容器
// 进入legacyRenderSubtreeIntoContainer函数
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
legacyRenderSubtreeIntoContainer(未完)
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>, //null
children: ReactNodeList, //App
container: Container, //容器
forceHydrate: boolean, //服务器渲染相关 false
callback: ?Function, //回调
) {
.....
// 此时_reactRootContainer还不存在!!!
const maybeRoot = container._reactRootContainer;
let root: FiberRoot;
// _reactRootContainer不存在,进入if
if (!maybeRoot) {
/**
* 初始化,
* _reactRootContainer一定不存在,因为初始化的时候没有这个属性
* 所以从DOM容器创建root
*/
// 从dom容器创建root
// 进入legacyCreateRootFromDOMContainer函数
root = legacyCreateRootFromDOMContainer(
container, //dom
children, //App
parentComponent, //null
callback,
forceHydrate, //false
);
}
.......
legacyCreateRootFromDOMContainer
function legacyCreateRootFromDOMContainer(
container: Container, //dom
initialChildren: ReactNodeList, //App
parentComponent: ?React$Component<any, any>, //父组件 null
callback: ?Function,
isHydrationContainer: boolean, //forceHydrate false
): FiberRoot {
.....
else {
// 清除所有兄弟接节点
// 即先清除<div id="root">下的所有内容
// 应该是不允许root节点下的原来内容,会用后面的React$Element填充
let rootSibling;
while ((rootSibling = container.lastChild)) {
//removeChild清除
container.removeChild(rootSibling);
}
// 获取root实例后,对root执行callback
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(root);
originalCallback.call(instance);
};
}
// 创建容器对象
// 这个函数上篇文章有讲过,不再赘述
const root = createContainer(
container,
LegacyRoot,
null, // hydrationCallbacks
false, // isStrictMode
false, // concurrentUpdatesByDefaultOverride,
'', // identifierPrefix
noopOnRecoverableError, // onRecoverableError
null, // transitionCallbacks
);
// 添加属性
container._reactRootContainer = root; //此时maybeRoot才会有值
// 标记
markContainerAsRoot(root.current, container);
// container
const rootContainerElement =
container.nodeType === COMMENT_NODE ? container.parentNode : container;
// 事件委托处理
// 这个函数上篇文章有讲过,不再赘述
listenToAllSupportedEvents(rootContainerElement);
// 初始化不应该批量挂载
// 返回的结果要么是undefined | updateContainer()的结果
flushSync(() => {
//进入updateContainer函数
updateContainer(initialChildren, root, parentComponent, callback);
});
return root;
}
}
updateContainer(未完)
export function updateContainer(
element: ReactNodeList, //app
container: OpaqueRoot, //ReactDOMRoot
parentComponent: ?React$Component<any, any>, //null
callback: ?Function,//callback
): Lane {
// 获取current
// 此时ReactDOMRoot.current就是FiberHostRoot对象
const current = container.current;
// 当前时间戳
// 进入requestEventTime函数
const eventTime = requestEventTime();
.....
requestEventTime
export function requestEventTime() {
// 判断有哪个权限
// executionContext 1表示有,0表示无
// RenderContext 1表示有,0表示无
// CommitContext 1表示有,0表示无
// 使用 | 来添加权限
// 使用 & 来判断权限
//这里就当我们不知道这些具体的值,我们假设没有通过这个if
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
return now();
}
//执行到这里,currentEventTime就是NoTimestamp
//源码:let currentEventTime: number = NoTimestamp;
if (currentEventTime !== NoTimestamp) {
return currentEventTime;
}
//所以还是return的now()
currentEventTime = now();
return currentEventTime;
}
updateContainer(未完)
....
// 创建一个优先级变量(车道模型)
//来看requestUpdateLane这个函数是个啥
const lane = requestUpdateLane(current);
....
requestUpdateLane
export function requestUpdateLane(fiber: Fiber): Lane {
// 获取到当前渲染的模式:sync mode(同步模式) 或 concurrent mode(并发模式)
const mode = fiber.mode;
fiber.mode是多少呢?
答案是3 那3代表什么?
1,普通模式,同步渲染
2,并发模式,异步渲染
3,严格模式,用来检测是否存在废弃API
4,性能测试模式,用来检测哪里存在性能问题
参考司徒正美大佬文章zhuanlan.zhihu.com/p/54037407
......
//我们继续
//这里已知mode=3 ConcurrentMode=1 所以不会走这个if
if ((mode & ConcurrentMode) === NoMode) {
return (SyncLane: Lane);
} else if (
//也不会走这个if
!deferRenderPhaseUpdateToNextBatch &&
(executionContext & RenderContext) !== NoContext &&
workInProgressRootRenderLanes !== NoLanes
) {
return pickArbitraryLane(workInProgressRootRenderLanes);
}
//requestCurrentTransition()返回null
const isTransition = requestCurrentTransition() !== NoTransition;
if (isTransition) {
....
}
//获取当前更新优先级,因为还没有进行相应的操作,所以当前优先级是NoLane 0
const updateLane: Lane = (getCurrentUpdatePriority(): any);
//不成立
if (updateLane !== NoLane) {
return updateLane;
}
/*
这里简单说一下getCurrentEventPriority作用:获取window.event,
如果是undefined那么返回DefaultEventPriority,反之拿着事件类型执行getEventPriority(),
getEventPriority内部就是switch判断给原生事件分个类然后返回React事件类型
*/
//这里返回的是DefaultEventPriority 16
const eventLane: Lane = (getCurrentEventPriority(): any);
return eventLane;
updateContainer(未完)
//requestUpdateLane返回了16
const lane = requestUpdateLane(current);
.....
// 因为parentComponent此时是null,所以context是emptyContextObject
const context = getContextForSubtree(parentComponent);
// 最开始container不存在context属性
if (container.context === null) {
// 赋值emptyContextObject
container.context = context;
} else {
container.pendingContext = context;
}
// 根据车道优先级, 创建update对象, 并加入fiber.updateQueue.pending队列
// 得到update={
// eventTime,
// lane,
// // export const UpdateState = 0;
// // export const ReplaceState = 1;
// // export const ForceUpdate = 2;
// // export const CaptureUpdate = 3;
// tag: UpdateState,
// payload: null,
// callback: null,
// next: null, //指向updateQueue中的下一个update
// }
const update = createUpdate(eventTime, lane);
// element就是App
update.payload = {element};
// callback存在
callback = callback === undefined ? null : callback;
if (callback !== null) {
......
// 将callback函数挂载到update对象上
update.callback = callback;
}
//进入enqueueUpdate函数
const root = enqueueUpdate(current, update, lane);
.....
enqueueUpdate
export function enqueueUpdate<State>(
fiber: Fiber,
update: Update<State>, //update对象
lane: Lane, //调度优先级
): FiberRoot | null {
// 获取当前fiber身上的updateQueue对象
// 当在初始化时,fiber.updateQueue是有值的
// 在ReactFiberRoot.old.js 202行,initializeUpdateQueue()时进行了挂载queue
/**
* fiber.updateQueue: UpdateQueue<State> = {
baseState: fiber.memoizedState, //前一次的计算结果
firstBaseUpdate: null,//上一个更新流程中被跳过的开头的update
lastBaseUpdate: null,//上一个更新流程中被跳过的结尾的update
shared: {
// 指向最新 且完整的环状链条
// A1 -> A2 -> A3
// | |
// pending(A6) -> A5 -> A4
// pending是一个完整的Queue
pending: null,
lanes: NoLanes,
hiddenCallbacks: null,
},
callbacks: null,
};
*/
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// 如果是卸载,那么就返回null
return null;
}
//在上篇文章的initializeUpdateQueue函数中queue对象上的share就是updateQueue.shared
//因为queue最终挂载到了fiber.updateQueue上
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
if (isUnsafeClassRenderPhaseUpdate(fiber)) {
// This is an unsafe render phase update. Add directly to the update
// queue so we can process it immediately during the current render.
/**
* 以下操作就是:
* 首次会自己指向自己
* 后续就将循环指向,因为update也越来越多了,sharedQueue.pending永远是最新的
* pending.next则指向最老的update
*/
const pending = sharedQueue.pending;// shared.pending指向该链表的最新的update对象
if (pending === null) {
// 说明是`首次更新`, 需要`创建`循环链表
update.next = update;
} else {
// `不是首次更新`, 那就把update对象`插入`到循环链表中
/**
* chain = a -> b -> c
* pending = c pending.next = a
* update = d d为新入队的任务
* update.next = pending.next 即是a
* pending.next = update; 即是d
* 执行sharedQueue.pending = update;
* 此时pending为d,pending.next为a
* 最终:a-> b -> c -> d
*/
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
// Update the childLanes even though we're most likely already rendering
// this fiber. This is for backwards compatibility in the case where you
// update a different component during render phase than the one that is
// currently renderings (a pattern that is accompanied by a warning).
// return root
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
//但是根据debug走还是走的这里的return
//暂时有点不懂enqueueConcurrentClassUpdate这个函数,后面看懂了我再更新一下
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
updateContainer(完)
....
if (root !== null) {
// scheduleWork 就是 scheduleUpdateOnFiber
// 进入scheduleUpdateOnFiber函数
scheduleUpdateOnFiber(root, current, lane, eventTime);
// 进入entangleTransitions函数
entangleTransitions(root, current, lane);
}
return lane;
scheduleUpdateOnFiber(未完)
export function scheduleUpdateOnFiber(
root: FiberRoot, //root
fiber: Fiber, //FiberHostRoot对象
lane: Lane, //lane
eventTime: number, //事件发生时间戳
) {
......
// 标记root有待更新
// 进入markRootUpdated函数
markRootUpdated(root, lane, eventTime);
......
markRootUpdated
export function markRootUpdated(
root: FiberRoot,
updateLane: Lane,
eventTime: number,
) {
root.pendingLanes |= updateLane;
//如果不是空闲lane
if (updateLane !== IdleLane) {
root.suspendedLanes = NoLanes;
root.pingedLanes = NoLanes;
}
const eventTimes = root.eventTimes;
// laneToIndex()计算lane的前导0的个数
const index = laneToIndex(updateLane);
eventTimes[index] = eventTime;
}
scheduleUpdateOnFiber(未完)
.....
//性能
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
......
}
// 性能
if (enableTransitionTracing) {
......
}
//workInProgressRoot:null
//此时还没有开启双缓存树
if (root === workInProgressRoot) {
......先不管
}
// 确保根被调度
ensureRootIsScheduled(root, eventTime);
总结
暂时先讲到这里,下一篇文章将进入ensureRootIsScheduled函数,这个函数是React进去render阶段前的准备工作
这一部分主要是React获取到fiber后,获取eventTime,事件类型已经创建updateQueue