请参考:
https://github.com/a8397550/react-source-share.git
行数
https://github.com/a8397550/react-source-share/blob/master/default/react-dom-default.js
下面源码分析建立在以下HTML的代码的基础上
<!-- /dist/index.html -->
<!doctype html>
<html>
<head>
<title>react</title>
<script src="./react.js"></script>
<script src="./react-dom.js"></script>
<style>
.classA {
color: red;
}
.classA .classB {
color: blue;
}
</style>
</head>
<body>
<div id="root"><div></div>
</div>
<script>
console.log("React", React);
console.log("ReactDOM", ReactDOM);
var DivSon = React.createElement("div", { className: 'classB', onClick: function(){ alert("点击事件哦") } }, "777");
var DivSonA = React.createElement("span", { className: 'classC' }, "这里是一个span");
// props.children
// ref的使用
// setState如何触发
var Div = React.createElement("div", { className: 'classA' }, DivSon, 999, DivSonA);
ReactDOM.render(Div, document.getElementById('root'));
</script>
</body>
</html>
Render 初始化分析
render: function (element, container, callback)
源码所在行数 25156
(function () {
// 自执行函数,验证container是一个DOM元素
var isValid = isValidContainer(container);
if (!isValid) {
{
throw ReactError(Error('Target container is not a DOM element.'));
}
}
})();
/**
* node存在 && node.nodeType === 1 返回true
* node存在 && node.nodeType === 9 返回true
* node存在 && node.nodeType === 11 返回true
* node存在 && node.nodeType === 8 && node.nodeValue === ' react-mount-point-unstable '
* 返回true
* 其他情况返回 false
* 此处的 && || 的用法
* && 的用法是 前面的结果为true 就使用后面的结果
* || 的用法是 前面的结果是false 就使用后面的结果
*/
function isValidContainer(node) {
return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable '));
}
{
// 验证DOM元素,是否已经被绑定ReactDOM绑定过
// 如果DOM元素是已被ReactDOM.render过的元素的话,给出警告,否则继续执行下面的代码
!!container._reactHasBeenPassedToCreateRootDEV ? warningWithoutStack$1(false, 'You are calling ReactDOM.Render() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. ' + 'Did you mean to call root.Render(element)?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot') : void 0;
}
源码所在行数 25167
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback)
源码所在行数 25086
container._reactRootContainer
container.firstChild[internalInstanceKey]
{
// 验证container有没有被react初始化过,
// 或者container的第一个子元素有没有被初始化过,
// 或者有没有使用document.body作为容器,
// 满足上面条件的话,给出对应的警告
topLevelUpdateWarnings(container);
warnOnInvalidCallback = function (callback, callerName) {
!(callback === null || typeof callback === 'function') ? warningWithoutStack$1(false, '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback) : void 0;
};
// callback 如果不是null 也不是一个function的话,给出警告
warnOnInvalidCallback(callback === undefined ? null : callback, 'render');
}
topLevelUpdateWarnings = function (container) {
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
// 进入if的条件时container._reactRootContainer存在并且container不是一个注释,而是一个元素
var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer._internalRoot.current);
if (hostInstance) {
!(hostInstance.parentNode === container) ? warningWithoutStack$1(false, '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.') : void 0;
}
}
// 初始化时值为false
var isRootRenderedBySomeReact = !!container._reactRootContainer;
// 获取container元素节点下的第一个子节点
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}
// 如果当前节点<div></div> 此时rootEl值为null
// 如果当前节点<div> </div> 此时rootEl值为#text类型节点
// 如果当前节点<div><div></div></div> 此时rootEl值为div节点
var rootEl = getReactRootElementInContainer(container);
function getInstanceFromNode$1(node) {
// internalInstanceKey 是一个常量,在react-dom被加载时初始化
// '__reactInternalInstance$' + Math.random().toString(36).slice(2);
// 假设internalInstanceKey当前值是"__reactInternalInstance$q0avznii6gi"
var inst = node[internalInstanceKey];
// 初始化时inst值为undefined;
if (inst) {
if (inst.tag === HostComponent || inst.tag === HostText) {
return inst;
} else {
return null;
}
}
return null;
}
// 验证node具备不具备"internalInstanceKey"属性,初始化时,node是没有那个属性的
// 初始化时此值为false
var hasNonRootReactChild = !!(rootEl && getInstanceFromNode$1(rootEl));
// 初始化时 !(!false || false) 结果为 false
// 当三元表达式值为true时,给出警告
!(!hasNonRootReactChild || isRootRenderedBySomeReact) ? warningWithoutStack$1(false, '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.') : void 0;
// 满足3个false的条件, 给出警告
// container.nodeType === 1
// container.tagName 存在
// container.tagName.toUpperCase() === 'BODY'
// 作者的用意大概是说,如果你使用document.body做容器的话,会给你一个警告
!(container.nodeType !== ELEMENT_NODE || !container.tagName || container.tagName.toUpperCase() !== 'BODY') ? warningWithoutStack$1(false, '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.') : void 0;
};
// 默认的时候 undefined
var root = container._reactRootContainer;
// fiber [faɪbə] 构造的意思
var fiberRoot = void 0;
if (!root) {
root = container._reactRootContainer =
legacyCreateRootFromDOMContainer(container, forceHydrate);
// ...
} else {
// ...
}
function legacyCreateRootFromDOMContainer(container, forceHydrate)
此方法会返回一个ReactSyncRoot的实例对象
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
function shouldHydrateDueToLegacyHeuristic(container) {
function getReactRootElementInContainer(container) {
if (!container) {
return null;
}
if (container.nodeType === DOCUMENT_NODE) {
return container.documentElement;
} else {
return container.firstChild;
}
}
var rootElement = getReactRootElementInContainer(container);
// 子元素存在
// 并且类型 nodeType = 1
// 并且hasAttribute('data-reactroot') 存在
// hasAttribute是DOM的方法,可以验证他的属性存在不存在
return !!(rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME));
}
// 初始化时 forceHydrate = false
var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// 在以下状态时不执行
// container首个子元素存在并且nodeType = 1,并且此子元素"data-reactroot"属性存在
if (!shouldHydrate) {
// 删除container下所有子节点
var warned = false;
var rootSibling = void 0;
while (rootSibling = container.lastChild) {
{
// 首次发现子节点的nodeType = 1,并且具备"data-reactroot"属性,给出警告
if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
warned = true;
warningWithoutStack$1(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);
}
}
{
// 满足某个条件,给出提示,暂不分析
// 此处可能和react的ssr(服务端渲染 server side render)有关
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
warnedAboutHydrateAPI = true;
lowPriorityWarning$1(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.');
}
}
// LegacyRoot 默认是0 shouldHydrate 是一个boolean值
// LegacyRoot 是一个全局变量
// 此时假设 shouldHydrate = false;
return new ReactSyncRoot(container, LegacyRoot, shouldHydrate);
}
ReactSyncRoot 实例
this._internalRoot = new fiberRootNode
this._internalRoot.current = new FiberNode(…)
this._internalRoot.current.stateNode = this._internalRoot;
/**
* @description 构造对象 ReactSyncRoot
* @param {HTMLElement} container
* @param {number} tag 最初是0
* @param {boolean} hydrate
* this._internalRoot = new fiberRootNode
*/
function ReactSyncRoot(container, tag, hydrate) {
var root = createFiberRoot(container, tag, hydrate);
this._internalRoot = root;
}
function createFiberRoot(containerInfo, tag, hydrate) {
var root = new FiberRootNode(containerInfo, tag, hydrate);
// uninitializedFiber [ˌʌnɪˈnɪʃəˌlaɪzd] 未初始化
// initializedFiber [ɪˈnɪʃəlaɪzd] 已初始化
var uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
return root;
}
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null;
this.containerInfo = containerInfo;
this.pendingChildren = null;
this.pingCache = null;
// 全局变量,初始化时为 0
this.finishedExpirationTime = NoWork;
this.finishedWork = null;
// 全局变量初始为 -1
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.hydrate = hydrate;
this.firstBatch = null;
this.callbackNode = null;
this.callbackExpirationTime = NoWork;
this.firstPendingTime = NoWork;
this.lastPendingTime = NoWork;
this.pingTime = NoWork;
// 全局变量
if (enableSchedulerTracing) {
// 全局方法 unstable_getThreadID
// 注意,它的出处在React源码中
/*
function unstable_getThreadID () {
// threadIDCounter 初始值为0
return ++threadIDCounter;
}
*/
this.interactionThreadID = unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
}
function createHostRootFiber(tag) {
var mode = void 0;
// 常量 ConcurrentRoot = 2
if (tag === ConcurrentRoot) {
// ConcurrentMode 常量4
// BatchedMode 常量 2
// StrictMode 常量 1
// ConcurrentMode | BatchedMode | StrictMode 值等于 7
// 注意 这里是位运算
mode = ConcurrentMode | BatchedMode | StrictMode;
} else if (tag === BatchedRoot) {
// 注意 这里是位运算
mode = BatchedMode | StrictMode;
} else {
// 初始化时,NoMode值为0
mode = NoMode;
}
// 初始化时 enableProfilerTimer = true && isDevToolsPresent = false
if (enableProfilerTimer && isDevToolsPresent) {
// ProfileMode 常量值 8
mode |= ProfileMode;
}
var createFiber = function (tag, pendingProps, key, mode) {
return new FiberNode(tag, pendingProps, key, mode);
};
// HostRoot 全局常量 常量值3
return createFiber(HostRoot, null, null, mode);
}
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; // [tʃaɪld] 子元素
this.sibling = null; // [ˈsɪblɪŋ] 下一个元素
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
// 当前DIFF类型 默认0 ,4 update,8 Deletion,2 Placement,6 PlacementAndUpdate
this.effectTag = NoEffect;
// 要修改的FiberNode链
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
if (enableProfilerTimer) {
// 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._debugID = debugCounter++;
this._debugSource = null;
this._debugOwner = null;
this._debugIsCurrentlyTiming = false;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}