ReactDOM.render
开始
创建ReactRoot
,并且根据情况调用root.legacy_renderSubtreeIntoContainer
或者root.render
,前者是遗留的 API 将来应该会删除,根据ReactDOM.render
的调用情况也可以发现parentComponent
是写死的null
DOMRenderer.unbatchedUpdates
制定不使用batchedUpdates
,因为这是初次渲染,需要尽快完成。
ReactDOM = {
render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
)
},
}
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: DOMContainer,
forceHydrate: boolean,
callback: ?Function,
) {
let root: Root = (container._reactRootContainer: any)
if (!root) {
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
)
if (typeof callback === 'function') {
const originalCallback = callback
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot)
originalCallback.call(instance)
}
}
// Initial mount should not be batched.
DOMRenderer.unbatchedUpdates(() => {
if (parentComponent != null) {
// 一般不会出现
} else {
root.render(children, callback)
}
})
} else {
// 有root的情况
}
return DOMRenderer.getPublicRootInstance(root._internalRoot)
}
首先会创建ReactRoot
对象,然后调用他的render
方法
创建ReactRoot
的时候会调用DOMRenderer.createContainer
创建FiberRoot
,在后期调度更新的过程中这个节点非常重要
这两个数据结构看这里
function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): Root {
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container)
// First clear any existing content.
if (!shouldHydrate) {
let warned = false
let rootSibling
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling)
}
}
// Legacy roots are not async by default.
const isConcurrent = false
return new ReactRoot(container, isConcurrent, shouldHydrate)
}
function ReactRoot(
container: Container,
isConcurrent: boolean,
hydrate: boolean,
) {
const root = DOMRenderer.createContainer(container, isConcurrent, hydrate)
this._internalRoot = root
}
ReactRoot.prototype.render = function(
children: ReactNodeList,
callback: ?() => mixed,
): Work {
const root = this._internalRoot
const work = new ReactWork()
callback = callback === undefined ? null : callback
if (__DEV__) {
warnOnInvalidCallback(callback, 'render')
}
if (callback !== null) {
work.then(callback)
}
DOMRenderer.updateContainer(children, root, null, work._onCommit)
return work
}
其中DOMRenderer
是react-reconciler/src/ReactFiberReconciler
,他的updateContainer
如下在这里计算了一个时间,这个时间叫做expirationTime
,顾名思义就是这次更新的 超时时间。
关于时间是如何计算的看这里
然后调用了updateContainerAtExpirationTime
,在这个方法里调用了scheduleRootUpdate
就非常重要了
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
const current = container.current
const currentTime = requestCurrentTime()
const expirationTime = computeExpirationForFiber(currentTime, current)
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
callback,
)
}
export function updateContainerAtExpirationTime(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
const current = container.current
const context = getContextForSubtree(parentComponent)
if (container.context === null) {
container.context = context
} else {
container.pendingContext = context
}
return scheduleRootUpdate(current, element, expirationTime, callback)
}
开始调度
首先要生成一个update
,不管你是setState
还是ReactDOM.render
造成的 React 更新,都会生成一个叫update
的对象,并且会赋值给Fiber.updateQueue
关于update
请看这里
然后就是调用scheduleWork
。注意到这里之前setState
和ReactDOM.render
是不一样,但进入schedulerWork
之后,就是任务调度的事情了,跟之前你是怎么调用的没有任何关系
function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
callback: ?Function,
) {
const update = createUpdate(expirationTime)
update.payload = { element }
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
}
enqueueUpdate(current, update)
scheduleWork(current, expirationTime)
return expirationTime
}
results matching ""
No results matching ""
扫码添加Jokcy,更多更新更优质的前端学习内容不断更新中,期待与你一起成长!