create-react-app创建的项目中,是调用ReactDOM.render
const initApp = () => {
ReactDOM.render(<div></div>, document.getElementById('root'))
}
initApp()
最后调用的是ReactRoot的render方法。
ReactRoot详解
function ReactRoot(
container: DOMContainer,
isConcurrent: boolean,
hydrate: boolean,
) {
// 这个 root 指的是 FiberRoot
const root = createContainer(container, isConcurrent, hydrate);
this._internalRoot = root;
}
// 作用是检查是否在render方法中传入callback,有传就放入ReactWork中的_callbacks
// 然后调用updateContainer
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
// 这里指 FiberRoot
const root = this._internalRoot;
// ReactWork 的功能就是为了在组件渲染或更新后把所有传入
// ReactDom.render 中的回调函数全部执行一遍
const work = new ReactWork();
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'render');
}
// 如果有 callback,就 push 进 work 中的数组
if (callback !== null) {
work.then(callback);
}
// work._onCommit 就是用于执行所有回调函数的
updateContainer(children, root, null, work._onCommit);
return work;
};
// 调用updateContainer
ReactRoot.prototype.unmount = function(){
return updateContainer(null, root, null, work._onCommit);
}
//
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(){
// parentComponent, children, callback传入
return updateContainer(children, root, parentComponent, work._onCommit);
}
ReactRoot.prototype.createBatch = function(){
const batch = new ReactBatch(this);
// internalRoot.firstBatch存在,就插已有batch的尾巴上。否者将firstBatch设置为这个新batch,并设置batch的next为null
return batch;
}
createContainer最终由createFiberRoot返回,
export function createFiberRoot(
containerInfo: any,
isConcurrent: boolean,
hydrate: boolean,
): FiberRoot {
// FiberRootNode 内部创建了很多属性
const root: FiberRoot = (new FiberRootNode(containerInfo, hydrate): any);
// 创建一个 root fiber
// fiber 其实也会组成一个树结构,内部使用了单链表树结构,每个节点及组件都会对应一个 fiber
// FiberRoot 和 Root Fiber 会互相引用
//通过以下代码找到 Fiber Root,对应着容器
// document.querySelector('#root')._reactRootContainer._internalRoot
const uninitializedFiber = createHostRootFiber(isConcurrent);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;
}
ReactWork详解
function ReactWork() {
// 数组。存放所有在回调函数
this._callbacks = null;
// 已经在执行回调的标志位
this._didCommit = false;
// 重新绑定 this,以防取不到正确的 this
this._onCommit = this._onCommit.bind(this);
}
// _onCommit方法,作用是将_callbacks中的回调挨个执行
ReactWork.prototype._onCommit = function(){}
// then方法,作用是将回调放入_callbacks中,如果_didCommit标志位为true, 则直接调用函数
ReactWork.prototype.then = function(){}
ReactBatch详解
function ReactBatch(root: ReactRoot) {
const expirationTime = computeUniqueAsyncExpiration();
this._expirationTime = expirationTime;
this._root = root;
this._next = null;
this._callbacks = null;
this._didComplete = false;
this._hasChildren = false;
this._children = null;
this._defer = true;
}
// 调用updateContainerAtExpirationTime,返回reactworker
ReactWork.prototype.render = function(){}
// then方法,作用是将回调放入_callbacks中,如果_didComplete标志位为true, 则直接调用函数
ReactWork.prototype.then = function(){}
// Complete方法,作用是将_callbacks中的回调挨个执行
ReactWork.prototype._onComplete = function(){}
// commit方法,作用是找到最尾巴的ReactRoot,调用render方法(?所以这里其实是为了解决多次setstate重新渲染的问题?)
ReactWork.prototype.commit = function(){}
updateContainer详解
其实就是计算了expirationTime,然后调用updateContainerAtExpirationTime
updateContainerAtExpirationTime详解
先获取context。然后调用scheduleRootUpdate
scheduleRootUpdate详解
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// 创建一个 update,就是内部有几个属性的对象
const update = createUpdate(expirationTime);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
// callback的检查
callback = callback === undefined ? null : callback;
if (callback !== null) {
warningWithoutStack(
typeof callback === 'function',
'render(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callback,
);
update.callback = callback;
}
// 执行 fiber 上的副作用(会将上个useEffect的return先执行再执行新的回调)
flushPassiveEffects();
// 把 update 入队,内部就是一些创建或者获取 queue(链表结构),然后给链表添加一个节点的操作
// 这里面有两个队列,queue1 = fiber.updateQueue; queue2 = alternate.updateQueue;
enqueueUpdate(current, update);
// 就是scheduleUpdateOnFiber函数,
scheduleWork(current, expirationTime);
return expirationTime;
}
export function scheduleUpdateOnFiber(
fiber: Fiber,
expirationTime: ExpirationTime,
) {
// 判断是否是无限循环update
checkForNestedUpdates();
// dev环境下的检查
warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);
// 找到rootFiber并遍历更新子节点的expirationTime
const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);
if (root === null) {
warnAboutUpdateOnUnmountedFiberInDEV(fiber);
return;
}
// NoWork表示无更新操作
root.pingTime = NoWork;
// 检查是否有高优先级任务打断当前正在执行的任务
checkForInterruption(fiber, expirationTime);
// 测试环境修改标志位记录更新
recordScheduleUpdate();
// 如果是同步任务的过期时间的话(Sync是最大的31位整型1073741823)
if (expirationTime === Sync) {
// 如果还未渲染
if (workPhase === LegacyUnbatchedPhase) {
// 批量更新时,render是要保持同步的,但布局的更新要延迟到批量更新的末尾才执行
// 调用workLoop进行循环单元更新
let callback = renderRoot(root, Sync, true);
while (callback !== null) {
callback = callback(true);
}
} else {
// render后,立即执行调度任务
scheduleCallbackForRoot(root, ImmediatePriority, Sync);
// 当前没有update时
if (workPhase === NotWorking) {
// Flush the synchronous work now, wnless we're already working or inside
// a batch. This is intentionally inside scheduleUpdateOnFiber instead of
// scheduleCallbackForFiber to preserve the ability to schedule a callback
// without immediately flushing it. We only do this for user-initated
// updates, to preserve historical behavior of sync mode.
// 刷新同步任务队列
flushImmediateQueue();
}
}
} else {
// TODO: computeExpirationForFiber also reads the priority. Pass the
// priority as an argument to that function and this one.
const priorityLevel = getCurrentPriorityLevel();
if (priorityLevel === UserBlockingPriority) {
// This is the result of a discrete event. Track the lowest priority
// discrete update per root so we can flush them early, if needed.
// TODO: 不太明白这里的意义
if (rootsWithPendingDiscreteUpdates === null) {
rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);
} else {
const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);
if (
lastDiscreteTime === undefined ||
lastDiscreteTime > expirationTime
) {
rootsWithPendingDiscreteUpdates.set(root, expirationTime);
}
}
}
scheduleCallbackForRoot(root, priorityLevel, expirationTime);
}
}
export const scheduleWork = scheduleUpdateOnFiber;
接下来分析scheduleCallbackForRoot