一、源码学习
1. ReactDOM.render 对外暴露代码位于源文件:
// react-dom/src/client/ReactDOM.js
import {
......
render,
......
} from './ReactDOMLegacy';
export {
......
findDOMNode,
hydrate,
render,
unmountComponentAtNode,
......
};
2. 查看最原始的ReactDOM.reader方法,发现其支持3个参数:
- element(来自React.createElement创建的VDOM)
- container(H5根节点)
- callback(可选)
// react-dom/src/client/ReactDOMLegacy.js
export function render(
element: React$Element<any>,
container: Container,
callback: ?Function,
) {
......
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
3. 继续查看legacyRenderSubtreeIntoContainer:
function legacyRenderSubtreeIntoContainer(
parentComponent: ?React$Component<any, any>,
children: ReactNodeList,
container: Container,
forceHydrate: boolean,
callback: ?Function,
) {
......
let root: RootType = (container._reactRootContainer: any);
let fiberRoot;
if (!root) {
// Initial mount
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);
});
} else {
......
// Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
源码中,if-else中 fiberRoot这段是IF和ELSE中是一样的,只是最后的更新方式不一样。
该方法判断是否是首次初始化(判断root是否存在):
- 不存在则先创建root = legacyCreateRootFromDOMContainer,然后 updateContainer;
- 存在则直接 updateContainer;
4. 我们来查看是如何创建root的:
- 清空container所有内容;
- 创建root;
function legacyCreateRootFromDOMContainer(
container: Container,
forceHydrate: boolean, // 调用render时该参数为false
): RootType {
// shouldHydrate = false
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// 清空container所有内容
if (!shouldHydrate) {
let warned = false;
let rootSibling;
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling);
}
}
return createLegacyRoot(
container,
shouldHydrate ? {hydrate: true}: undefined
);
}
// react-dom/src/client/ReactDOMRoot.js
export function createLegacyRoot(
container: Container,
options?: RootOptions,
): RootType {
// LegacyRoot = 0
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
function ReactDOMBlockingRoot(
container: Container,
tag: RootTag,
options: void | RootOptions,
) {
this._internalRoot = createRootImpl(container, tag, options);
}
function createRootImpl(
container: Container,
tag: RootTag,
options: void | RootOptions, // undefined
) {
......
// 重点
const root = createContainer(container, tag, false, null);
markContainerAsRoot(root.current, container);
......
return root;
}
方法 createRootImpl 中的两个重点调用:
- createContainer;
- markContainerAsRoot;(之后会在【legacyRenderSubtreeIntoContainer】中判断root是否已经初始化过)
5. 继续查看如何创建 root 的:
// react-reconciler/src/ReactFiberReconciler.js
export function createContainer(
containerInfo: Container,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): OpaqueRoot {
return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
}
6. createFiberRoot干了4件事:
创建root,类型是 FiberRootNode;
- 创建 Fiber;
- 初始化更新队列;
- 返回创建好的 root ;
// react-reconciler/src/ReactFiberRoot.js
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber);
return root;
}
FiberRootNode结构:
// react-reconciler/src/ReactFiberRoot.js
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.context = null;
this.hydrate = hydrate; // false
......
}
createHostRootFiber -> createFiber -> 返回 FiberNode结构:
// react-reconciler/src/ReactFiber.js
export function createHostRootFiber(tag: RootTag): Fiber {
......
// mode = NoMode
return createFiber(HostRoot, null, null, mode);
}
const createFiber = function(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies_old = null;
this.mode = mode;
......
}
initializeUpdateQueue:
// react-reconciler/src/ReactUpdateQueue.js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
},
effects: null,
};
fiber.updateQueue = queue;
}
创建好 root 后,返回到【3. legacyRenderSubtreeIntoContainer】,调用 updateContainer 进行渲染。
root 是个对象:
root._internalRoot = FiberRootNode; // legacyRenderSubtreeIntoContainer中 this._internalRoot 赋值
root._internalRoot.current = FiberNode; // createFiberRoot
root._internalRoot.current.stateNode = root._internalRoot;// createFiberRoot
二、回顾一下 render 的整体调用链
三、总结
ReactDOM.render源码就先学习到这,至于 updateContainer ,涉及到 Fiber 及其 Scheduler 机制,之后会继续学习。Fiber 也是 React v16 推出的一个重点,提升了 React 的渲染性能。