这一章节就来讲讲ReactDOM.render()
方法的内部实现与流程吧。
因为初始化的源码文件部分所涵盖的内容很多,包括创建渲染
、更新渲染
、Fiber树
的创建与diff
,element
的创建与插入,还包括一些优化算法,所以我就整个的React
执行流程画了一个简单的示意图。
React源码执行流程图
从图中我们很清晰的看到ReactDOM.render()
之后我们的组件具体干了什么事情,那么我们进入源码文件一探究竟吧。
// packages/react-dom/src/client/ReactDOMLegacy.js
export function render(
element: React$Element<any>, // 经过babel解析后的element
container: Container, // 根组件节点: document.getElementById('root')..
callback: ?Function,// 回调
) {
// 做合法容器的验证(根组件)
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
// 开发模式下
if (__DEV__) {
const isModernRoot =
isContainerMarkedAsRoot(container) &&
container._reactRootContainer === undefined;
if (isModernRoot) {
console.error(
'You are calling ReactDOM.render() on a container that was previously ' +
'passed to ReactDOM.createRoot(). This is not supported. ' +
'Did you mean to call root.render(element)?',
);
}
}
// 返回 legacyRenderSubtreeIntoContainer
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
所以当前render
函数仅仅只是做了部分逻辑,阅读React
源码,给你一个直观的感受就是他拆分的颗粒度
非常的细,很多重复命名
的函数,可能是见名知意的变量名只有那么几个常见的组合吧,这也是React作者
的用心良苦吧。
追根究底我们还是得看一看legacyRenderSubtreeIntoContainer
究竟干了些不为人知的事情呢
legacyRenderSubtreeIntoContainer
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>, // 父级组件
children: ReactNodeList, // 当前元素
container: Container, // 容器 eg:getElementById('root')
forceHydrate: boolean, callback: ?Function,
) {
if (__DEV__) {
topLevelUpdateWarnings(container);
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}
// 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) {
// 初始化挂载
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);
};
}
// 不必要的批量更新
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
} else {
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
const originalCallback = callback;
callback = function() {
const instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
}
// 批量更新
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
- 有根节点的情况下,我们判定为非首次渲染状态,执行
updateContainer
- 没有根节点的情况下,我们判定为首次渲染,接着去创建根节点,执行
legacyCreateRootFromDOMContainer
,拿到了root
之后,我们会去触发执行updateContainer
legacyCreateRootFromDOMContainer
function legacyCreateRootFromDOMContainer(
container: Container, // 容器