總結
render
// 渲染函數
function render(element, container, callback) {
// element: React.Component元素這個是react的工作, 等會看
// container: 這個傳入的dom元素
// callback: 沒傳
if (!isValidContainer(container)) { // 判斷是否爲合理的dom元素
{
throw Error( "Target container is not a DOM element." );
}
}
{
// 是否被標記問react的根元素
var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
if (isModernRoot) {
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)?');
}
}
// 將dom節點渲染到container中
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}
isValidContainer
function isValidContainer(node) {
// node: dom元素
return !!(node &&
(node.nodeType === ELEMENT_NODE || // 元素
node.nodeType === DOCUMENT_NODE || // 整個文檔
node.nodeType === DOCUMENT_FRAGMENT_NODE || // 輕量級的Document對象
node.nodeType === COMMENT_NODE && // 註釋元素 就必須判斷 node-value了 TODO: 這裏不理解註釋元素,是如何處理的
node.nodeValue === ' react-mount-point-unstable ')
);
}
isContainerMarkedAsRoot
var randomKey = Math.random().toString(36).slice(2);
var internalInstanceKey = '__reactFiber$' + randomKey;
var internalPropsKey = '__reactProps$' + randomKey;
var internalContainerInstanceKey = '__reactContainer$' + randomKey;
var internalEventHandlersKey = '__reactEvents$' + randomKey;
function precacheFiberNode(hostInst, node) {
node[internalInstanceKey] = hostInst;
}
function markContainerAsRoot(hostRoot, node) {
// hostRoot: RootFiber
// node: containerDom
// containerDom.__reactContainer = RootFiber
node[internalContainerInstanceKey] = hostRoot;
}
function unmarkContainerAsRoot(node) {
node[internalContainerInstanceKey] = null;
}
// 判斷節點是否有internalContainerInstanceKey: TODO: 是否可以理解爲判斷根組件?
function isContainerMarkedAsRoot(node) {
return !!node[internalContainerInstanceKey];
}
legacyRenderSubtreeIntoContainer
// 過時的, 將子樹渲染到Container中
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
// parentComponent: 父組件 null
// children: 子組件 element
// container: 掛載元素 container
// forceHydrate: 是否爲服務端渲染ssr
// callback: 回調函數
{
topLevelUpdateWarnings(container); // 關於container 輸出錯誤
warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render'); // 判斷callback是否爲一個函數
}
// fb開發者逗笑我了, 看源碼唄, 這句話, 爲啥打印了.
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
// member of intersection type." Whyyyyyy.
var root = container._reactRootContainer; // 獲取根節點
var fiberRoot; // fiber的根節點
if (!root) { // 沒獲取到, 證明這個就是根節點
// Initial mount
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
// FiberRootNode
fiberRoot = root._internalRoot; // createContainer(container, tag, hydrate);
if (typeof callback === 'function') { // 處理回調函數
var originalCallback = callback;
callback = function () {
var instance = getPublicRootInstance(fiberRoot);
originalCallback.call(instance);
};
} // Initial mount should not be batched.
unbatchedUpdates(function () { // TODO: 執行更新流程, 明天再看
updateContainer(children, fiberRoot, parentComponent, callback); // 這一步就把我們的children 渲染上去了
});
} else {
fiberRoot = root._internalRoot;
if (typeof callback === 'function') {
var _originalCallback = callback;
callback = function () {
var instance = getPublicRootInstance(fiberRoot);
_originalCallback.call(instance);
};
} // Update
updateContainer(children, fiberRoot, parentComponent, callback);
}
return getPublicRootInstance(fiberRoot);
}
topLevelUpdateWarnings
{
topLevelUpdateWarnings = function (container) {
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
// 首次執行的時候, container還不是根節點, 不會走到這裏
// 判斷是否爲根節點, 如果是的話, 類型不是註釋節點
var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer._internalRoot.current);
if (hostInstance) {
if (hostInstance.parentNode !== container) {
error('render(...): It looks like the React-rendered content of this ' + 'container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + 'ReactDOM.unmountComponentAtNode to empty a container.');
}
}
}
var isRootRenderedBySomeReact = !!container._reactRootContainer;
var rootEl = getReactRootElementInContainer(container); // 獲取根元素, 第一遍渲染, 這裏返回了null
var hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl)); // false
if (hasNonRootReactChild && !isRootRenderedBySomeReact) { // 首次不會走這裏
error('render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.');
}
if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') {
// 如果是文檔掛在了Body上, 報錯. 因爲有很多的第三方腳本和瀏覽器插件都放在body下面, react一更新就沒了
error('render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.');
}
};
}
getReactRootElementInContainer
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOCUMENT_NODE) { // 如果是document, 就返回document元素
return container.documentElement;
} else {
return container.firstChild; // 否則返回第一個元素, 但是這裏是null, 因爲後面沒有元素了
}
}
warnOnInvalidCallback
function warnOnInvalidCallback$1(callback, callerName) { // 判斷callback是否爲一個函數
{
if (callback !== null && typeof callback !== 'function') {
error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
}
}
}
legacyCreateRootFromDOMContainer
// 從container中創建根節點
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
// container: 掛載節點
// forceHydrate: 是否進行強制ssr
// 判斷是否進行ssr
var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container); // First clear any existing content.
if (!shouldHydrate) { // 不是ssr, 這裏開始清空元素
var warned = false;
var rootSibling;
while (rootSibling = container.lastChild) { // 判斷根節點下面是否含有子元素
{
if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) { // 如果含有子元素, 並且子元素是react元素, 報錯
warned = true;
error('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 (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) { // 處理ssr?
warnedAboutHydrateAPI = true;
// React 18將會停止render 進行ssr更新, 並提供hydrate進行.
warn('render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v18. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.');
}
}
return createLegacyRoot(container, shouldHydrate ? {
hydrate: true
} : undefined);
}
shouldHydrateDueToLegacyHeuristic
function shouldHydrateDueToLegacyHeuristic(container) { // TODO: 返回根節點是否應該更新?
var rootElement = getReactRootElementInContainer(container); // 獲取根節點
return !!(rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME));
}
createLegacyRoot
// 創建根節點
function createLegacyRoot(container, options) {
debugger
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}
// 創建老的一種根節點
function ReactDOMBlockingRoot(container, tag, options) {
this._internalRoot = createRootImpl(container, tag, options);
}
createRootImpl
// 創建根節點接口
function createRootImpl(container, tag, options) {
// Tag is either LegacyRoot or Concurrent Root // tag表示, 你創建了一個老版本的root, 還是一個同時更新的root
// 因爲, 我們沒有傳入任何options
var hydrate = options != null && options.hydrate === true; // false, 不是ssr
var hydrationCallbacks = options != null && options.hydrationOptions || null; // null
var mutableSources = options != null && options.hydrationOptions != null && options.hydrationOptions.mutableSources || null; // null
var root = createContainer(container, tag, hydrate);
// root就是FiberRootNode
// FiberRootNode.containerInfo 指向了 container的dom元素
// FiberRootNode.current 指向了 RootFiber
// RootFiber.stateNode 指向了 FiberRootNode
markContainerAsRoot(root.current, container);
// container的dom元素.__reactContainer$ = RootFiber
var containerNodeType = container.nodeType;
{
var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
listenToAllSupportedEvents(rootContainerElement); // 這裏不再繼續看事件的篩選添加過程.
}
if (mutableSources) { // 這是是false
for (var i = 0; i < mutableSources.length; i++) {
var mutableSource = mutableSources[i];
registerMutableSourceForHydration(root, mutableSource);
}
}
return root;
}
function createContainer(containerInfo, tag, hydrate, hydrationCallbacks) {
// contanerInfo: container的dom節點
// tag: 表示老的root, 還是新的同時更新的root節點
// hydrate: ssr
return createFiberRoot(containerInfo, tag, hydrate);
}
// 創建Fiber根節點
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
// 這裏創建的fiber的根節點
var root = new FiberRootNode(containerInfo, tag, hydrate);
// root上掛載container的信息
// stateNode is any.
// 這裏纔是創建真正的fiber
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
initializeUpdateQueue(uninitializedFiber);
return root;
}
FiberRootNode
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag; // 表示老的root, 還是新的同時更新的root節點
this.containerInfo = containerInfo; // container的dom節點
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate; // 是否ssr
this.callbackNode = null;
this.callbackPriority = NoLanePriority;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
{
this.mutableSourceEagerHydrationData = null;
}
{
this.interactionThreadID = tracing.unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
{
switch (tag) {
case BlockingRoot:
this._debugRootType = 'createBlockingRoot()';
break;
case ConcurrentRoot:
this._debugRootType = 'createRoot()';
break;
case LegacyRoot:
this._debugRootType = 'createLegacyRoot()';
break;
}
}
}
createLaneMap
function createLaneMap(initial) {
// Intentionally pushing one by one.
// https://v8.dev/blog/elements-kinds#avoid-creating-holes
var laneMap = [];
for (var i = 0; i < TotalLanes; i++) {
laneMap.push(initial);
}
return laneMap;
}
createHostRootFiber
// 根據tab, 創建fiber的根節點
function createHostRootFiber(tag) {
var mode;
if (tag === ConcurrentRoot) { // 根據tab指定mode
mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
mode = BlockingMode | StrictMode;
} else {
mode = NoMode;
}
if ( isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
// 創建真正的節點
return createFiber(HostRoot, null, null, mode);
}
var createFiber = function (tag, pendingProps, key, mode) {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
FiberNode
function FiberNode(tag, pendingProps, key, mode) {
// 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 = null;
this.mode = mode; // Effects
this.flags = NoFlags;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
{
// Note: The following is done to avoid a v8 performance cliff.
//
// Initializing the fields below to smis and later updating them with
// double values will cause Fibers to end up having separate shapes.
// This behavior/bug has something to do with Object.preventExtension().
// Fortunately this only impacts DEV builds.
// Unfortunately it makes React unusably slow for some applications.
// To work around this, initialize the fields below with doubles.
//
// Learn more about this here:
// https://github.com/facebook/react/issues/14365
// https://bugs.chromium.org/p/v8/issues/detail?id=8538
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
// This won't trigger the performance cliff mentioned above,
// and it simplifies other profiler code (including DevTools).
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
}
{
// This isn't directly used but is handy for debugging internals:
this._debugID = debugCounter++;
this._debugSource = null;
this._debugOwner = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this); // 讓對象變得不能擴展
}
}
}
initializeUpdateQueue
function initializeUpdateQueue(fiber) { // 初始化更新隊列
var queue = {
baseState: fiber.memoizedState, // null
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null
},
effects: null
}; // 啥啥都是null, 把隊列返回出去
fiber.updateQueue = queue;
}