记录学习过程,如有错误欢迎指出
前置知识
- Fiber
- React生命周期钩子
有关Fiber的内容,大家可以Google一下,网上有很多讲解,关于Fiber我有可能后面会写篇文章
本篇文章只是给大家讲解执行顺序和逻辑,真正执行时的数值结果等,还请大家自己debug一下查看实际执行结果
推荐的Debug方式
下载react-dom.development.js && react.development.js两个文件,然后引入html的方式直接开干,我自己感觉用脚手架比较麻烦,用文件引入的方式,只要文件和当前源码版本一直,那么基本上都是大同小异的
大家可以前往这个地址下载www.bootcdn.cn/
开始之前(必看)
React version 18.2.0
DEV代码可以忽略,下面正文我使用省略号就代表是dev相关的代码,包含hydrate字样的也请忽略,那是服务端渲染相关,望周知
我使用深度优先(🐶)的方式讲解:即遇到函数先进入函数,执行完函数后,又退回之前的函数.而不是在一个函数中讲完了再讲函数中执行的函数(希望能听懂我在说什么^v^)
因为使用了深度优先的方式讲解,
耦合比较重,不建议跳着看
入口
const root = ReactDOM.createRoot(document.querySelector("#app"));
root.render(<App />);
createRoot
入口就是
createRoot这个方法了
// 入口 最终返回ReactDOMRoot对象
function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,//可选
//可以看到createRoot其实可以接收第二个参数的,但是我们通常并没有使用到
): RootType {
.....
return createRootImpl(container, options);
//进入createRootImpl
}
createRootImpl as createRoot(未完)
//这个createRoot不是上一个createRoot
export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// 判断是否是有效容器
if (!isValidContainer(container)) {
throw new Error('createRoot(...): Target container is not a DOM element.');
}
....
//定义变量
let isStrictMode = false;
let concurrentUpdatesByDefaultOverride = false;
let identifierPrefix = '';
let onRecoverableError = defaultOnRecoverableError;
let transitionCallbacks = null;
.....
// 创建容器对象
// 最终返回的是具有current:FiberHostRoot的FiberRoot对象
// 进入createContainer()
const root = createContainer(
container, //Element | Document | DocumentFragment,
ConcurrentRoot, //export const ConcurrentRoot = 1;
null,
isStrictMode, //false
concurrentUpdatesByDefaultOverride, //false
identifierPrefix, //'';
onRecoverableError, //全局错误报告函数
transitionCallbacks, //null
);
.....
createContainer
export function createContainer(
containerInfo: Container,//Element | Document | DocumentFragment,
tag: RootTag,//export const ConcurrentRoot = 1;
hydrationCallbacks: null | SuspenseHydrationCallbacks, //null
isStrictMode: boolean, //false
concurrentUpdatesByDefaultOverride: null | boolean,//false
identifierPrefix: string,//''
onRecoverableError: (error: mixed) => void,//全局错误报告函数
transitionCallbacks: null | TransitionTracingCallbacks, //null
): OpaqueRoot {
const hydrate = false;
const initialChildren = null;
// 返回挂载了current属性的FiberRoot对象
// 进入createFiberRoot
return createFiberRoot(
containerInfo,
tag,
hydrate,
initialChildren,
hydrationCallbacks,
isStrictMode,
concurrentUpdatesByDefaultOverride,
identifierPrefix,
onRecoverableError,
transitionCallbacks,
);
}
createFiberRoot
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
initialChildren: ReactNodeList,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
isStrictMode: boolean,
concurrentUpdatesByDefaultOverride: null | boolean,
identifierPrefix: string,
onRecoverableError: null | ((error: mixed) => void),
transitionCallbacks: null | TransitionTracingCallbacks,
): FiberRoot {
//new FiberRootNode这里,就是new一个新的FiberRootNode对象,大家可以自己进入看下,
//里面就是一些自身属性的声明,有点繁杂了,大家自己去看一下吧
const root: FiberRoot = (new FiberRootNode(
containerInfo,
tag,
hydrate,
identifierPrefix,
onRecoverableError,
): any);
.....
// 创建FiberHostRoot对象
// FiberRootNode和FiberHostRoot是两个东西,不要混淆
const uninitializedFiber = createHostRootFiber(
tag,
isStrictMode,
concurrentUpdatesByDefaultOverride,
);
// 在FiberRootNode对象上挂载current属性,值为FiberHostRoot对象
root.current = uninitializedFiber;
// 在FiberHostRoot对象上挂载stateNode:FiberRootNode
/*
现在的样子大概就是:
uninitializedFiber:{
stateNode:{
root:{
current:FiberHostRoot,
....
}
}
}
*/
uninitializedFiber.stateNode = root;
if (enableCache) {
......
} else {
const initialState: RootState = {
element: initialChildren,//查看createContainer函数中,会发现是null
isDehydrated: hydrate,//不管
cache: (null: any), // not enabled yet
};
//memoizedState可以理解为`已经记录的state`
//uninitializedFiber是什么,好好回想一下!
//是FiberHostRoot!
uninitializedFiber.memoizedState = initialState;
}
// 初始化updateQueue队列!
// 进入initializeUpdateQueue
initializeUpdateQueue(uninitializedFiber);
......
initializeUpdateQueue
export function initializeUpdateQueue<State>(fiber: Fiber/*FiberHostRoot*/): void {
const queue: 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,
};
// 挂载queue
fiber.updateQueue = queue;
}
执行完毕,返回上一个函数createFiberRoot
.....
// 返回挂载了FiberHostRoot对象的FiberRoot对象
// 这里返回的是root 并不是 uninitializedFiber!!!
return root;
}
执行完毕,返回上一个函数createContainer
继续返回上一个函数createRoot(createRootImpl)
createRootImpl as createRoot(未完)
.....
/**
* 传入的root.current就是在createContainer内部创建的FiberHostRoot对象
*
* markContainerAsRoot在当前container身上添加一个internalContainerInstanceKey属性
* 值为root.current
*/
markContainerAsRoot(root.current, container);
// rootContainerElement: container
const rootContainerElement: Document | Element | DocumentFragment =
container.nodeType === COMMENT_NODE
? (container.parentNode: any)
: container;
// 事件委托处理
// 进入listenToAllSupportedEvents
listenToAllSupportedEvents(rootContainerElement);
.....
listenToAllSupportedEvents
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
// 判断rootContainerElement身上是否有listeningMarker属性
if (!(rootContainerElement: any)[listeningMarker]) {
// 如果没有,则添加为ture
(rootContainerElement: any)[listeningMarker] = true;
// allNativeEvents:获取所有原生事件
allNativeEvents.forEach(domEventName => {
// We handle selectionchange separately because it
// doesn't bubble and needs to be on the document.
if (domEventName !== 'selectionchange') {
if (!nonDelegatedEvents.has(domEventName)) {
listenToNativeEvent(domEventName, false, rootContainerElement);
}
listenToNativeEvent(domEventName, true, rootContainerElement);
}
});
const ownerDocument =
(rootContainerElement: any).nodeType === DOCUMENT_NODE
? rootContainerElement
: (rootContainerElement: any).ownerDocument;
if (ownerDocument !== null) {
// The selectionchange event also needs deduplication
// but it is attached to the document.
if (!(ownerDocument: any)[listeningMarker]) {
(ownerDocument: any)[listeningMarker] = true;
listenToNativeEvent('selectionchange', false, ownerDocument);
}
}
}
}
//进入listenToNativeEvent
listenToNativeEvent
export function listenToNativeEvent(
domEventName: DOMEventName,
isCapturePhaseListener: boolean, //false
target: EventTarget, //根容器节点
): void {
......
let eventSystemFlags = 0;
// 判断是否是监听捕获阶段
if (isCapturePhaseListener) {
// 是的话,打上标记
eventSystemFlags |= IS_CAPTURE_PHASE;
}
// 将事件绑定到根节点上
// 进入addTrappedEventListener
addTrappedEventListener(
target,
domEventName,
eventSystemFlags,
isCapturePhaseListener,
);
}
addTrappedEventListener
function addTrappedEventListener(
targetContainer: EventTarget, //根容器节点
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags, //事件flag
isCapturePhaseListener: boolean, //false
isDeferredListenerForLegacyFBSupport?: boolean,
) {
// createEventListenerWrapperWithPriority()优先创建事件监听器包装器
//返回:一个事件方法bind调用后的值
let listener = createEventListenerWrapperWithPriority(
targetContainer,
domEventName,
eventSystemFlags,
);
// If passive option is not supported, then the event will be
// active and not passive.
let isPassiveListener = undefined;
if (passiveBrowserEventsSupported) {
/**
* 这里进行对一个特殊事件的判断,因为浏览器的干预,
* 所以需要对touchstart|touchmove|wheel事件进行单独处理
*/
if (
domEventName === 'touchstart' ||
domEventName === 'touchmove' ||
domEventName === 'wheel'
) {
isPassiveListener = true;
}
}
// 不变
targetContainer =
/**
* enableLegacyFBSupport:false
初次调用addTrappedEventListener时没有传递isDeferredListenerForLegacyFBSupport的值,所以是null
条件不成立,targetContainer还是targetContainer
如果启用了传统FB那么targetContainer就是targetContainer.ownerDocument了
*/
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
? (targetContainer: any).ownerDocument
: targetContainer;
let unsubscribeListener;
if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
const originalListener = listener;
listener = function(...p) {
//移除监听
removeEventListener(
targetContainer,
domEventName,
unsubscribeListener,
isCapturePhaseListener,
);
return originalListener.apply(this, p);
};
}
// TODO: There are too many combinations here. Consolidate them.
if (isCapturePhaseListener) {
if (isPassiveListener !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
targetContainer,
domEventName,
listener,
isPassiveListener,
);
} else {
......
}
} else {
......
}
}
执行完毕,返回上一层函数createRoot
createRootImpl as createRoot(完)
......
// 返回ReactDomRoot对象,该对象的_internalRoot属性指向FiberRoot
// 进入ReactDOMRoot
return new ReactDOMRoot(root);
}
ReactDOMRoot
/**
* 返回一个ReactDOMRott对象,
* 该对象的_internalRoot属性指向FiberRoot
* 这个函数就这么简单
*/
function ReactDOMRoot(internalRoot: FiberRoot) {
this._internalRoot = internalRoot;
}
最后
总结一下createRoot(createRootImpl),主要就是创建Fiber,事件委托处理,最后将root身上添加一个属性_internalRoot,这个属性指向root
记录学习过程,如有错误欢迎指出