ReactDOM.render
通常是如下图使用,在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析,文中的源代码部分为了方便阅读将__DEV__
部分的代码移除掉了。
ReactDOM.render(
<App />,
document.getElementById('root')
);
render
位于:react-dom/src/client/ReactDOMLegacy.js
export function render(
element: React$Element<any>, container: Container, callback: ?Function,
) {
// 验证container是否为有效的DOM节点
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
返回了一个legacyRenderSubtreeIntoContainer函数,这里注意有5个参数
parentComponent
: 父组件因为是初次创建所以为null。
children
: 传入的ReactElement
container
: 渲染React的DOM容器
forceHydrate
: 判断是否需要协调,在服务端渲染的情况下已渲染的DOM结构是类似的因此可以在对比后进行复用。在服务端渲染的情况下使用ReactDOM.hydrate()与 render() 相同只是forceHydrate会标记为true。
callback
: 渲染完成后的回调函数
legacyRenderSubtreeIntoContainer
位于:react-dom/src/client/ReactDOMLegacy.js
作用:
- 判断是否为初次渲染,如果是就创建root并将root._internalRoot赋值给fiberRoot同时封装callback回调,然后调用unbatchedUpdates立即更新子节点。
- 如果不是第一次渲染则进入正常的updateContainer流程。
- 最后getPublicRootInstance(fiberRoot)返回公开的 Root 实例对象。
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
) {
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
let root: RootType = (container._reactRootContainer: any);
let fiberRoot;
if (!root) {
// Initial mount 初次渲染创建FiberRoot
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback