ReactDom
1,根据react的测试用例,直接打断点,看下reactdom.render()到底是如何工作的,这里可以先忽略DEV, 因为这里大多数都是一些检测,一些warning提示,可以先理清出整体脉络,然后再去关注这些点.
文件地址:
packages/react-dom/src/tests/ReactDOM-test.js
打上断点
document.body.appendChild(container);
try {
// 这里打上断点
ReactDOM.render( , container);
buttonRef.click();
expect(count).toBe(1);
} finally {
document.body.removeChild(container);
}
文件地址:
packages/react-dom/src/client/ReactDOM.js
render(
element: React$Element,
container: DOMContainer,
callback: ?Function,
) {
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
},
可以看到这里没有做过多的处理,然后直接就转到了legacyRenderSubtreeIntoContainer
这里的legacy是遗产遗留的意思,不知道是不是以后会放弃掉。
function legacyRenderSubtreeIntoContainer(
// null
parentComponent: ?React$Component,
// 子元素,这里是div
children: ReactNodeList,
// 这里是容器
container: DOMContainer,
// 是否是服务器服务端
forceHydrate: boolean,
// null
callback: ?Function,
) {
// TODO: Ensure all entry points contain this check
// 准备做,确保所有的都先检查是否是正确的
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
// 在开发模式下,检测,如果不符合一堆warning
if (__DEV__) {
topLevelUpdateWarnings(container);
}
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
// 是否存在当前的根节点
let root: Root = (container._reactRootContainer: any);
if (!root) {
// Initial mount
// 创建根节点
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
);
// null
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Initial mount should not be batched.
// 初始化不能是成批的,这里是向真实的dom里边去插入dom
DOMRenderer.unbatchedUpdates(() => {
// 有没有真实的dom父元素
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
});
} else {
// null
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
originalCallback.call(instance);
};
}
// Update,
// 和上面同样的操作
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
root.render(children, callback);
}
}
// 获取container的元素
return DOMRenderer.getPublicRootInstance(root._internalRoot);
}
可以看到如下图,这里的container就是一个对象,如图所示:
可以看到当没有root的时候,首先通过legacyCreateRootFromDOMContainer来创建root,然后在进行其他操作。
所以我们去跟进 legacyCreateRootFromDOMContainer
function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): Root {
// 是不是服务器渲染
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// First clear any existing content.
// 首先清空container的内容
if (!shouldHydrate) {
let warned = false;
let rootSibling;
// 循环清空
while ((rootSibling = container.lastChild)) {
if (__DEV__) {
if (
!warned &&
rootSibling.nodeType === ELEMENT_NODE &&
(rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
) {
warned = true;
warningWithoutStack(
false,
'render(): Target node has markup rendered by React, but there ' +
'are unrelated nodes as well. This is most commonly caused by ' +
'white-space inserted around server-rendered markup.',
);
}
}
container.removeChild(rootSibling);
}
}
if (__DEV__) {
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
warnedAboutHydrateAPI = true;
lowPriorityWarning(
false,
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'will stop working in React v17. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
}
}
// Legacy roots are not async by default.
const isAsync = false;
return new ReactRoot(container, isAsync, shouldHydrate);
}
这里比较简单,就是简单的通过container.lastchild循环来清除container的所有内容,因为我们的属于首次渲染,container里边不包含任何元素,所以直接执行到了
return new ReactRoot(container, isAsync, shouldHydrate);
通过ReactRoot来创建一个root。
下一步直接追踪到ReactRoot
function ReactRoot(container: Container, isAsync: boolean, hydrate: boolean) {
const root = DOMRenderer.createContainer(container, isAsync, hydrate);
this._internalRoot = root;
}
这里也很简单,通过DOMRenderer.createContainer创建root,然后挂载到_internalRoot上面。
然后可以到DOMRenderer.createContainer,看下里边是做什么?
通过阅读代码,可以发现DOMRenderer是在react-reconciler导出的,
可以非常容易的追踪到DOMRenderer.createContainer的代码
文件地址:
packages/react-reconciler/src/ReactFiberReconciler.js
export function createContainer(
containerInfo: Container,
isAsync: boolean,
hydrate: boolean,
): OpaqueRoot {
return createFiberRoot(containerInfo, isAsync, hydrate);
}
可以看到代码非常的简单,就是简单的调用来createFiberRoot,
然后去寻找createFiberRoot所在
文件地址:
packages/react-reconciler/src/ReactFiberRoot.js
export function createFiberRoot(
containerInfo: any,
isAsync: boolean,
hydrate: boolean,
): FiberRoot {
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 首先创建hostRoot
const uninitializedFiber = createHostRootFiber(isAsync);
这里的代码也很简单,首先会去创建createHostRootFiber
暂时先到这里:)