// getRootHostContainer(1)
// url: src/react/packages/react-reconciler/src/ReactFiberHostContext.old.js
function getRootHostContainer(): Container {
// 获取实例
const rootInstance = requiredContext(rootInstanceStackCursor.current);
return rootInstance;
// 返回的结果 : div#root,就是挂载的元素
}
// 备注: 返回挂载的节点, div#root
//
// requiredContext
function requiredContext<Value>(c: Value | NoContextT): Value {
invariant(
c !== NO_CONTEXT,
'Expected host context to exist. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
return (c: any);
}
// 备注: 读取节点的内容
// getHostContext
function getHostContext(): HostContext {
const context = requiredContext(contextStackCursor.current);
return context;
}
// 备注: 返回contextStackCursor.current 的内容
const NO_CONTEXT: NoContextT = ({}: any);
// 备注: 主要获取创建dom元素的需要配置的参数。
// createInstance(2)
// url: src/react/packages/react-dom/src/client/ReactDOMHostConfig.js
export function createInstance(
type: string, // 元素的名称
props: Props, // props
rootContainerInstance: Container, // div#root
hostContext: HostContext, // "http://www.w3.org/1999/xhtml"
internalInstanceHandle: Object, // fiber 对象
): Instance {
let parentNamespace: string;
// 跳过
if (__DEV__) {
// 忽略
} else {
// 设置命名空间
//hostContext : "http://www.w3.org/1999/xhtml"
parentNamespace = ((hostContext: any): HostContextProd);
}
// 元素节点创建
const domElement: Instance = createElement(
type,
props,
rootContainerInstance,
parentNamespace,
);
// 设置fiber 和node 的关系
precacheFiberNode(internalInstanceHandle, domElement);
// 设置ndoe 和props的关系
updateFiberProps(domElement, props);
return domElement;
}
// 备注: 元素节点的创建, 设置元素和fiber, 元素和 props的关系。
// precacheFiberNode (3)
// url: src/react/packages/react-dom/src/client/ReactDOMComponentTree.js
export function precacheFiberNode(
hostInst: Fiber,
node: Instance | TextInstance | SuspenseInstance | ReactScopeInstance,
): void {
(node: any)[internalInstanceKey] = hostInst;
//
}
// 备注:节点的关系设置 node 节点和fiber 关系
// node
const randomKey = Math.random()
.toString(36)
.slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
// url: src/react/packages/react-dom/src/client/ReactDOMComponentTree.js
export function updateFiberProps(
node: Instance | TextInstance | SuspenseInstance,
props: Props,
): void {
(node: any)[internalPropsKey] = props;
}
// 备注: 设置node 和props 的关系
// props
const internalPropsKey = '__reactProps$' + randomKey;
const randomKey = Math.random()
.toString(36)
.slice(2);
// createElement (4)
// url: src/react/packages/react-dom/src/client/ReactDOMComponent.js
export function createElement(
type: string,
props: Object,
rootContainerElement: Element | Document,
parentNamespace: string,
): Element {
let isCustomComponentTag;
// 节点的创建
const ownerDocument: Document = getOwnerDocumentFromRootContainer(
rootContainerElement,
);
// namespace 的处理
let domElement: Element;
let namespaceURI = parentNamespace;
if (namespaceURI === HTML_NAMESPACE) { // 'http://www.w3.org/1999/xhtml';
// HTML_NAMESPACE:"http://www.w3.org/1999/xhtml"
// 处理
namespaceURI = getIntrinsicNamespace(type);
}
if (namespaceURI === HTML_NAMESPACE) {
// 正常路线
// script 的处理
if (type === 'script') {
const div = ownerDocument.createElement('div');
div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
// This is guaranteed to yield a script element.
const firstChild = ((div.firstChild: any): HTMLScriptElement);
domElement = div.removeChild(firstChild);
} else if (typeof props.is === 'string') {
// is 的处理
// $FlowIssue `createElement` should be updated for Web Components
domElement = ownerDocument.createElement(type, {is: props.is});
} else {
// 普通的类型创建
domElement = ownerDocument.createElement(type);
if (type === 'select') {
const node = ((domElement: any): HTMLSelectElement);
if (props.multiple) { // 设置multiple 属性
node.multiple = true;
} else if (props.size) { // 设置size 属性
node.size = props.size;
}
}
}
} else {
// 不是htmmns 时, 创建
domElement = ownerDocument.createElementNS(namespaceURI, type);
}
// 跳过的部分
// dev 部分,省略
return domElement;
}
// 备注: 根据命名空间参数,和元素明晨创建一个dom节点。
// getIntrinsicNamespace()
// url: src/react/packages/react-dom/src/shared/DOMNamespaces.js
export function getIntrinsicNamespace(type: string): string {
switch (type) {
case 'svg':
return SVG_NAMESPACE;
case 'math':
return MATH_NAMESPACE;
default:
return HTML_NAMESPACE;
}
}
// 备注: 命名空间的转换
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
// getOwnerDocumentFromRootContainer(5)
// url: src/react/packages/react-dom/src/client/ReactDOMComponent.js
function getOwnerDocumentFromRootContainer(
rootContainerElement: Element | Document, // container
): Document {
return rootContainerElement.nodeType === DOCUMENT_NODE
? (rootContainerElement: any) //
: rootContainerElement.ownerDocument; // window.document
// 多平台的兼容行
}
// 备注:多平台的兼容返回 window.document
// HostRoot节点的completeWork 中为一个空函数
// url: src/react/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
updateHostContainer = function(workInProgress: Fiber) {
// Noop
};
// 备注: hostRoot节点中的completeWork为一个空函数
// appendAllChildren (6)
// url: src/react/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
appendAllChildren = function(
parent: Instance, // dom
workInProgress: Fiber, // fiber
needsVisibilityToggle: boolean,
isHidden: boolean,
) {
// 只会执行第一次的dom 或text 处理, 第二层不处理
// 第一个child
let node = workInProgress.child;
while (node !== null) {
// 原生的dom 或文本
if (node.tag === HostComponent || node.tag === HostText) {// 。
appendInitialChild(parent, node.stateNode);
// 只执行一次, 执行一层
} else if (enableFundamentalAPI && node.tag === FundamentalComponent) {
// 跳过
// export const enableFundamentalAPI = false;
appendInitialChild(parent, node.stateNode.instance);
} else if (node.tag === HostPortal) {
// HostPortal 不处理
// 空
} else if (node.child !== null) { // 上述条件都不符合
// 更新为子节点
node.child.return = node;
node = node.child;
continue; // 跳出这个
}
// 这个子树已经结束了
if (node === workInProgress) {
return;
}
// 没有兄弟节点的时候
while (node.sibling === null) {
// 返回为null
if (node.return === null || node.return === workInProgress) {
return;
}
node = node.return;
}
// 遍历
node.sibling.return = node.return; // 不停的遍历兄弟节点
node = node.sibling;
}
};
// 备注: 遍历workInProgress 的子节点, 当为文本或dom元素,直接添加为传入的dom子节点, 类型为HostPortal 跳过
// 不添加, 其他类型时, 深度遍历, 直到文本节点或dom, 添加到添加为传入的dom子节点。
// 只添加一层, 不进行深度遍历
// 节点的添加
// url: src/react/packages/react-dom/src/client/ReactDOMHostConfig.js
export function appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
// 节点的挂载
parentInstance.appendChild(child);
}
// 备注: 节点的增加 : document.appendChild
// finalizeInitialChildren(7)
// url: src/react/packages/react-dom/src/client/ReactDOMHostConfig.js
export function finalizeInitialChildren(
domElement: Instance, // dom节点
type: string, // dom节点的字符标示
props: Props, // 属性
rootContainerInstance: Container, // div#root
hostContext: HostContext, //
): boolean {
// 设置属性
setInitialProperties(domElement, type, props, rootContainerInstance);
return shouldAutoFocusHostComponent(type, props); //节点是否需要聚焦
}
// 备注: 执行setInitialProperties 函数, 并将shouldAutoFocusHostComponent 的结果返回。
// setInitialProperties (8)
// url: src/react/packages/react-dom/src/client/ReactDOMComponent.js
export function setInitialProperties(
domElement: Element, // dom节点
tag: string, // dom节点的字符标示
rawProps: Object, // 属性
rootContainerElement: Element | Document, // div#root
): void {
// 是否是的组件
const isCustomComponentTag = isCustomComponent(tag, rawProps);
let props: Object;
switch (tag) { // 标签名称
case 'dialog': // dialog
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('cancel', domElement);
listenToNonDelegatedEvent('close', domElement);
props = rawProps;
break;
case 'iframe':
case 'object':
case 'embed':
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('load', domElement);
props = rawProps;
break;
case 'video':
case 'audio':
for (let i = 0; i < mediaEventTypes.length; i++) {
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
}
props = rawProps;
break;
case 'source': // source
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('error', domElement);
props = rawProps;
break;
case 'img':
case 'image':
case 'link':
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('error', domElement);
listenToNonDelegatedEvent('load', domElement);
props = rawProps;
break;
case 'details':
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('toggle', domElement);
props = rawProps;
break;
case 'input': // input 的处理
// 基础属性设置
ReactDOMInputInitWrapperState(domElement, rawProps);
props = ReactDOMInputGetHostProps(domElement, rawProps);
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('invalid', domElement);
if (!enableEagerRootListeners) { //true;
// export const enableEagerRootListeners = true;
ensureListeningTo(rootContainerElement, 'onChange', domElement);
}
break;
case 'option': // option
ReactDOMOptionValidateProps(domElement, rawProps);
props = ReactDOMOptionGetHostProps(domElement, rawProps);
break;
case 'select': // select
ReactDOMSelectInitWrapperState(domElement, rawProps);
props = ReactDOMSelectGetHostProps(domElement, rawProps);
// 直接挂载在dom元素上事件
listenToNonDelegatedEvent('invalid', domElement);
if (!enableEagerRootListeners) { // enableEagerRootListeners : ture;
// 事件怎么挂载?
// 直接挂载在dom元素上事件
ensureListeningTo(rootContainerElement, 'onChange', domElement);
}
break;
case 'textarea': // textarea 的处理
ReactDOMTextareaInitWrapperState(domElement, rawProps);
props = ReactDOMTextareaGetHostProps(domElement, rawProps);
listenToNonDelegatedEvent('invalid', domElement);
if (!enableEagerRootListeners) {
// 直接挂载在dom元素上事件
ensureListeningTo(rootContainerElement, 'onChange', domElement);
}
break;
default:
props = rawProps;
}
// 验证props
assertValidProps(tag, props);
// 属性设置
setInitialDOMProperties(
tag,
domElement,
rootContainerElement,
props,
isCustomComponentTag,
);
switch (tag) {
case 'input':
// 什么
track((domElement: any));
ReactDOMInputPostMountWrapper(domElement, rawProps, false);
break;
case 'textarea':
track((domElement: any));
ReactDOMTextareaPostMountWrapper(domElement, rawProps);
break;
case 'option':
ReactDOMOptionPostMountWrapper(domElement, rawProps);
break;
case 'select':
ReactDOMSelectPostMountWrapper(domElement, rawProps);
break;
default:
if (typeof props.onClick === 'function') {
// TODO: This cast may not be sound for SVG, MathML or custom elements.
trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
}
break;
}
}
// 备注: (1)对一些元素进行非代理的事件绑定, (2)props 的校验,(3)对dom元素设置属性,
// style, innerHTML, attribute,
// (4)其他处理
// isCustomComponent (9)
// url: src/react/packages/react-dom/src/shared/isCustomComponent.js
function isCustomComponent(tagName: string, props: Object) {
if (tagName.indexOf('-') === -1) { // 不具有- 是
return typeof props.is === 'string'; // 且props 中is属性值 为字符
}
switch (tagName) {
case 'annotation-xml':
case 'color-profile':
case 'font-face':
case 'font-face-src':
case 'font-face-uri':
case 'font-face-format':
case 'font-face-name':
case 'missing-glyph':
return false;
default:
return true;
}
}
// 备注: 是否是自定义组件/元素?
// listenToNonDelegatedEvent(9)
// 监听 进行不代理的事件绑定
// url: src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
export function listenToNonDelegatedEvent(
domEventName: DOMEventName, // 事件名称
targetElement: Element, // 触发源
): void {
const isCapturePhaseListener = false; // 在捕获阶段触发?
const listenerSet = getEventListenerSet(targetElement); // Set 的结构
const listenerSetKey = getListenerSetKey( // 将名称和是否捕获转换为key
domEventName,
isCapturePhaseListener,
); // 转化成的key
if (!listenerSet.has(listenerSetKey)) { // 不具有
addTrappedEventListener( // 后面再解析
targetElement, // 触发元素
domEventName, // 事件名称
IS_NON_DELEGATED, // export const IS_NON_DELEGATED = 1 << 1;
isCapturePhaseListener, //是否捕获
);
listenerSet.add(listenerSetKey); // 设置key
}
}
// 备注:直接在元素上绑定事件, 特定的事件直接绑定在元素上。
// getEventListenerSet (10)
// url: src/react/packages/react-dom/src/client/ReactDOMComponentTree.js
export function getEventListenerSet(node: EventTarget): Set<string> {
// 节点的监听set 获取
let elementListenerSet = (node: any)[internalEventHandlersKey]; // 生成的key
if (elementListenerSet === undefined) {
elementListenerSet = (node: any)[internalEventHandlersKey] = new Set();
// 是一个set 结构
}
return elementListenerSet;
}
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const randomKey = Math.random()
.toString(36)
.slice(2);
// 备注: 在dom 节点上增加internalEventHandlersKey 属性, 结构为一个Set , 储存当前
// 元素上挂载的事件
// getListenerSetKey
// url: src/react/packages/react-dom/src/events/DOMPluginEventSystem.js
export function getListenerSetKey(
domEventName: DOMEventName, // 事件名称
capture: boolean, // 是否捕获
): string {
return `${domEventName}__${capture ? 'capture' : 'bubble'}`;
}
// 备注: 将事件名称和 是否捕获 转化的字段,生成一个事件的key, 这么理解,一个元素只能挂载一个对应
// 的事件
// ReactDOMInputInitWrapperState(11)
import {
initWrapperState as ReactDOMInputInitWrapperState,
} from './ReactDOMInput';
// url: src/react/packages/react-dom/src/client/ReactDOMInput.js
export function initWrapperState(element: Element, props: Object) {
// 节点
const node = ((element: any): InputWithWrapperState); // 节点
// 默认值
const defaultValue = props.defaultValue == null ? '' : props.defaultValue;
node._wrapperState = {
initialChecked: // 初始checked
props.checked != null ? props.checked : props.defaultChecked,
initialValue: getToStringValue( // 获取值
props.value != null ? props.value : defaultValue,
),
controlled: isControlled(props),
};
}
// 备注: 对于input类型节点node 上挂载_wrapperState 属性,initialChecked: 优先取checked ,
//其次 defaultChecked, initialValue 优先值value, 其次 defaultValue,controlled
// 转化为string
// url : src/react/packages/react-dom/src/client/ToStringValue.js
export function getToStringValue(value: mixed): ToStringValue {
switch (typeof value) {
case 'boolean':
case 'number':
case 'object':
case 'string':
case 'undefined':
return value;
default:
// function, symbol are assigned as empty strings
return '';
}
}
// 备注: 转化为字符串
// url: src/react/packages/react-dom/src/client/ReactDOMInput.js
function isControlled(props) {
// checkbox 或radio
const usesChecked = props.type === 'checkbox' || props.type === 'radio';
return usesChecked ? props.checked != null : props.value != null;
}
// 备注:(1) type为checkbox 或radio 时, checked 不为空,其他情况下不为空
// ReactDOMInputGetHostProps (12)
import {
getHostProps as ReactDOMInputGetHostProps,
} from './ReactDOMInput';
// url: src/react/packages/react-dom/src/client/ReactDOMInput.js
export function getHostProps(element: Element, props: Object) {
const node = ((element: any): InputWithWrapperState); // 节点
const checked = props.checked; // 选中
// 返货hostProps
const hostProps = Object.assign({}, props, {
defaultChecked: undefined,
defaultValue: undefined,
value: undefined,
checked: checked != null ? checked : node._wrapperState.initialChecked, // 原来计算
});
return hostProps;
}
// 备注: 获取input 元素的props, 额外增加defaultChecked, defaultValue, value,
// 和checked
// ReactDOMOptionValidateProps (13)
import {
validateProps as ReactDOMOptionValidateProps,
} from './ReactDOMOption';
// url: src/react/packages/react-dom/src/client/ReactDOMOption.js
export function validateProps(element: Element, props: Object) {
if (__DEV__) {
// This mirrors the code path above, but runs for hydration too.
// 空函数
}
}
// 备注: 没有任何处理
import {
getHostProps as ReactDOMOptionGetHostProps,
} from './ReactDOMOption';
// 获取props
export function getHostProps(element: Element, props: Object) {
const hostProps = {children: undefined, ...props};
const content = flattenChildren(props.children);
// 处理children
if (content) {
hostProps.children = content;
}
return hostProps;
}
// 备注: 设置options 节点上的props, 在props的基础上增加children;
//
function flattenChildren(children) {
let content = '';
// 遍历
React.Children.forEach(children, function(child) {
if (child == null) {
return;
}
content += (child: any);
});
return content;
}
// 备注: 返回children , 如果是数组,直接对内部的元素进行拼接。
// ReactDOMSelectInitWrapperState (14)
import {
initWrapperState as ReactDOMSelectInitWrapperState,
getHostProps as ReactDOMSelectGetHostProps,
} from './ReactDOMSelect';
// initWrapperState
// url: src/react/packages/react-dom/src/client/ReactDOMSelect.js
export function initWrapperState(element: Element, props: Object) {
const node = ((element: any): SelectWithWrapperState);
node._wrapperState = {
wasMultiple: !!props.multiple,
};
}
// 备注: 设置select 的_wrapperState 属性。属性值为multiple
// url: src/react/packages/react-dom/src/client/ReactDOMSelect.js
export function getHostProps(element: Element, props: Object) {
return Object.assign({}, props, {
value: undefined,
});
}
// 备注: 获取select 的props; 在原来的基础上增加value: undefined;
// ReactDOMTextareaInitWrapperState (15)
import {
initWrapperState as ReactDOMTextareaInitWrapperState,
getHostProps as ReactDOMTextareaGetHostProps,
} from './ReactDOMTextarea';
// url: src/react/packages/react-dom/src/client/ReactDOMTextarea.js
export function initWrapperState(element: Element, props: Object) {
const node = ((element: any): TextAreaWithWrapperState); // 节点
let initialValue = props.value; // 数据
if (initialValue == null) { // 初始值为null
let {children, defaultValue} = props; // 获取children
if (children != null) { // children 非空
// export const disableTextareaChildren = false;
if (!disableTextareaChildren) { // true;
invariant(
defaultValue == null,
'If you supply `defaultValue` on a <textarea>, do not pass children.',
);
if (Array.isArray(children)) { // 数组
invariant(
children.length <= 1,
'<textarea> can only have at most one child.',
);
children = children[0]; // 获取第一个
}
defaultValue = children;
}
}
if (defaultValue == null) { // 默认值
defaultValue = '';
}
initialValue = defaultValue; // 初始值
}
node._wrapperState = {
initialValue: getToStringValue(initialValue),
};
}
// 备注: 获取textarae 的initialValue 值的对象。(1) 优先取value, (2) 再取children (3)
// 最后取值defaultValue
export function getHostProps(element: Element, props: Object) {
const node = ((element: any): TextAreaWithWrapperState);
const hostProps = {
...props,
value: undefined,
defaultValue: undefined,
children: toString(node._wrapperState.initialValue),
};
return hostProps;
}
// 备注: 对于textarea类型, props 包含原来的props , value, default, 和children;
// setInitialDOMProperties(16)
// url: src/react/packages/react-dom/src/client/ReactDOMComponent.js
function setInitialDOMProperties(
tag: string,
domElement: Element,
rootContainerElement: Element | Document,
nextProps: Object,
isCustomComponentTag: boolean,
): void {
// 遍历属性
for (const propKey in nextProps) {
// 过滤非自身属性
if (!nextProps.hasOwnProperty(propKey)) {
continue;
}
const nextProp = nextProps[propKey];
// style
if (propKey === STYLE) {
// 设置style
setValueForStyles(domElement, nextProp);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
// dangerouslySetInnerHTML
const nextHtml = nextProp ? nextProp[HTML] : undefined;
if (nextHtml != null) {
setInnerHTML(domElement, nextHtml);
}
} else if (propKey === CHILDREN) {
if (typeof nextProp === 'string') { // string
// children 属性非空或者 tag 不为textarea
const canSetTextContent = tag !== 'textarea' || nextProp !== ''; // 非空字符
if (canSetTextContent) {
setTextContent(domElement, nextProp);
}
} else if (typeof nextProp === 'number') {
// 数字
setTextContent(domElement, '' + nextProp); // 属性
}
} else if (
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
propKey === SUPPRESS_HYDRATION_WARNING
) {
// 没操作
// Noop
} else if (propKey === AUTOFOCUS) {
// 空
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
// 注册的事件
if (nextProp != null) {
if (!enableEagerRootListeners) { // enableEagerRootListeners: true
// 事件注册
// export const enableEagerRootListeners = true;
ensureListeningTo(rootContainerElement, propKey, domElement); // 同16版本有
// 差异, 不在这里注册事件
} else if (propKey === 'onScroll') { // scroll
listenToNonDelegatedEvent('scroll', domElement);
}
}
} else if (nextProp != null) {
// 其他的特性
setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag);
}
}
}
// 备注: 对dom 元素,进行style, children , dangerouslySetInnerHTML 和其他特性的设置
// setValueForStyles(17)
// url: src/react/packages/react-dom/src/shared/CSSPropertyOperations.js
export function setValueForStyles(node, styles) { // 节点 , style 对象
const style = node.style;
// 名称
for (let styleName in styles) { // 遍历
// 过滤非自己拥有的
if (!styles.hasOwnProperty(styleName)) {
continue;
}
// 游览器的
const isCustomProperty = styleName.indexOf('--') === 0; // z
// 样式值
const styleValue = dangerousStyleValue(
styleName, // 属性名
styles[styleName], // 属性值
isCustomProperty, //
);
if (styleName === 'float') { // float处理
styleName = 'cssFloat';
}
if (isCustomProperty) { // 自己
style.setProperty(styleName, styleValue);
} else {
style[styleName] = styleValue; // 其他情况
}
}
}
// 备注: 处理特殊的样式名,处理样式的数据,通过style 设置dom的样式 或者setProperty
// 设置样式
// dangerousStyleValue(18)
// url: src/react/packages/react-dom/src/shared/dangerousStyleValue.js
function dangerousStyleValue(name, value, isCustomProperty) {
// 空值校验
const isEmpty = value == null || typeof value === 'boolean' || value === '';
if (isEmpty) {
return '';
}
// name不在isCustomProperty 中,
// // 具有这个属性, 且在这个对象中
if (
!isCustomProperty && // 非--样式
typeof value === 'number' && // 是一个数字
value !== 0 && // 不是0
!(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])
) {
// px, 一些样式增加px;
return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
}
return ('' + value).trim();
}
// 备注:(1) 对一些样式值进行空值处理 (2) 判断特定样式,增加px;
// src/react/packages/react-dom/src/shared/CSSProperty.js
export const isUnitlessNumber = {
animationIterationCount: true,
borderImageOutset: true,
borderImageSlice: true,
borderImageWidth: true,
boxFlex: true,
boxFlexGroup: true,
boxOrdinalGroup: true,
columnCount: true,
columns: true,
flex: true,
flexGrow: true,
flexPositive: true,
flexShrink: true,
flexNegative: true,
flexOrder: true,
gridArea: true,
gridRow: true,
gridRowEnd: true,
gridRowSpan: true,
gridRowStart: true,
gridColumn: true,
gridColumnEnd: true,
gridColumnSpan: true,
gridColumnStart: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
tabSize: true,
widows: true,
zIndex: true,
zoom: true,
// SVG-related properties
fillOpacity: true,
floodOpacity: true,
stopOpacity: true,
strokeDasharray: true,
strokeDashoffset: true,
strokeMiterlimit: true,
strokeOpacity: true,
strokeWidth: true,
};
// setTextContent (19)
// url: src/react/packages/react-dom/src/client/setTextContent.js
const setTextContent = function(node: Element, text: string): void {
if (text) {
const firstChild = node.firstChild; //第一个节点
if (
firstChild &&
firstChild === node.lastChild && // 只有一个节点 与最后节点相等
firstChild.nodeType === TEXT_NODE // 节点类型为文本
) {
firstChild.nodeValue = text;
return;
}
}
node.textContent = text;
};
// 备注: (1)设置节点的context; (2)
// createMicrosoftUnsafeLocalFunction(20)
// url: src/react/packages/react-dom/src/shared/createMicrosoftUnsafeLocalFunction.js
const createMicrosoftUnsafeLocalFunction = function(func) {
// MSApp 存在 且 MSApp.execUnsafeLocalFunction 具有属性
if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
return function(arg0, arg1, arg2, arg3) {
MSApp.execUnsafeLocalFunction(function() {
return func(arg0, arg1, arg2, arg3);
});
};
} else {
// 测试没有MSApp
return func;
}
};
// 备注: 根据判断MSApp 和 MSApp.execUnsafeLocalFunction 是否存在,返回具体的函数
// setInnerHTML(21)
// url: src/react/packages/react-dom/src/client/setInnerHTML.js
const setInnerHTML = createMicrosoftUnsafeLocalFunction(function(
node: Element,
html: {valueOf(): {toString(): string, ...}, ...},
): void {
if (node.namespaceURI === Namespaces.svg) { // 命名空间的判断
// dev代码,已省略
if (!('innerHTML' in node)) {
reusableSVGContainer =
reusableSVGContainer || document.createElement('div');
reusableSVGContainer.innerHTML =
'<svg>' + html.valueOf().toString() + '</svg>';
const svgNode = reusableSVGContainer.firstChild;
while (node.firstChild) {
node.removeChild(node.firstChild);
}
while (svgNode.firstChild) {
node.appendChild(svgNode.firstChild);
}
return;
}
}
node.innerHTML = (html: any); // 直接设置html的内容
});
// 备注: 设置元素节点的innerHTML
// setValueForProperty(22)
// url: src/react/packages/react-dom/src/client/DOMPropertyOperations.js
export function setValueForProperty(
node: Element,
name: string,
value: mixed,
isCustomComponentTag: boolean,
) {
// 获取对应name的attribet对象
const propertyInfo = getPropertyInfo(name); // 获取名称的name 的对象描述
if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) {
// 可以被忽略的
return;
}
// 一些需要移除的特性
if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) {
value = null;
}
// 名称 或者 为空
if (isCustomComponentTag || propertyInfo === null) {
if (isAttributeNameSafe(name)) { // 符合要求
const attributeName = name;
if (value === null) { // 为空
node.removeAttribute(attributeName); // 清除值
} else {
node.setAttribute( // 设置
attributeName,
enableTrustedTypesIntegration ? (value: any) : '' + (value: any),
);
}
}
return;
}
const {mustUseProperty} = propertyInfo;
if (mustUseProperty) {
const {propertyName} = propertyInfo;
if (value === null) {
const {type} = propertyInfo;
(node: any)[propertyName] = type === BOOLEAN ? false : '';
} else {
// Contrary to `setAttribute`, object properties are properly
// `toString`ed by IE8/9.
(node: any)[propertyName] = value;
}
return;
}
// The rest are treated as attributes with special cases.
const {attributeName, attributeNamespace} = propertyInfo;
if (value === null) {
node.removeAttribute(attributeName);
} else {
const {type} = propertyInfo;
let attributeValue;
if (type === BOOLEAN || (type === OVERLOADED_BOOLEAN && value === true)) {
attributeValue = '';
} else {
if (enableTrustedTypesIntegration) {
attributeValue = (value: any);
} else {
attributeValue = '' + (value: any);
}
if (propertyInfo.sanitizeURL) {
sanitizeURL(attributeValue.toString());
}
}
if (attributeNamespace) {
node.setAttributeNS(attributeNamespace, attributeName, attributeValue);
} else {
node.setAttribute(attributeName, attributeValue);
}
}
}
// 备注: 设置原生dom元素特性。详细细节不注释了。
// getPropertyInfo (23)
// 获取dom 节点的特性
export function getPropertyInfo(name: string): PropertyInfo | null {
return properties.hasOwnProperty(name) ? properties[name] : null;
}
// 备注:获取dom 节点特定name的对应的attribute对象
// shouldIgnoreAttribute
// 是否是该被忽略的特性
// url: src/react/packages/react-dom/src/shared/DOMProperty.js
export function shouldIgnoreAttribute(
name: string,
propertyInfo: PropertyInfo | null,
isCustomComponentTag: boolean,
): boolean {
// 特性兑现非空
if (propertyInfo !== null) { // 非空
// export const RESERVED = 0;
return propertyInfo.type === RESERVED;
}
// 是自己
if (isCustomComponentTag) {
return false;
}
// 具有isCustomComponentTag 数据
if (
name.length > 2 &&
(name[0] === 'o' || name[0] === 'O') &&
(name[1] === 'n' || name[1] === 'N')
) {
return true;
}
return false;
}
//备注: 返回是否该被忽略的特性
// shouldRemoveAttributeWithWarning(24)
// 是否需要移除特性
// ulr: src/react/packages/react-dom/src/shared/DOMProperty.js
export function shouldRemoveAttributeWithWarning(
name: string, // 名称
value: mixed, // 数据
propertyInfo: PropertyInfo | null,
isCustomComponentTag: boolean,
): boolean {
// 不为空 // export const RESERVED = 0;
if (propertyInfo !== null && propertyInfo.type === RESERVED) {
return false;
}
switch (typeof value) { //
case 'function': // 函数
// $FlowIssue symbol is perfectly valid here
case 'symbol': // eslint-disable-line
return true;
case 'boolean': {
if (isCustomComponentTag) { // 自定义?
return false;
}
if (propertyInfo !== null) { // 非空
return !propertyInfo.acceptsBooleans; // 接收数据为boolean, s实际不为boolean
} else {
const prefix = name.toLowerCase().slice(0, 5); // 前面5个字符
return prefix !== 'data-' && prefix !== 'aria-';
}
}
default:
return false;
}
}
// 备注: value 为 函数或这boolean , 数据类型为boolean, 实际数据为非boolean。返回ture,
// shouldRemoveAttribute(25)
// 是否是需要移除的特性
export function shouldRemoveAttribute(
name: string,
value: mixed,
propertyInfo: PropertyInfo | null,
isCustomComponentTag: boolean,
): boolean {
// 值为空
if (value === null || typeof value === 'undefined') {
return true;
}
// 一些需要移除的特性
if (
shouldRemoveAttributeWithWarning(
name,
value,
propertyInfo,
isCustomComponentTag,
)
) {
return true;
}
// 自定义
if (isCustomComponentTag) {
return false;
}
// 非空
if (propertyInfo !== null) {
// export const enableFilterEmptyStringAttributesDOM = false;
if (enableFilterEmptyStringAttributesDOM) { // false , 不执行
if (propertyInfo.removeEmptyString && value === '') {
// dev代码, 已删除
return true;
}
}
switch (propertyInfo.type) {
case BOOLEAN:
return !value;
case OVERLOADED_BOOLEAN:
return value === false;
case NUMERIC:
return isNaN(value);
case POSITIVE_NUMERIC:
return isNaN(value) || (value: any) < 1;
}
}
return false;
}
// 备注: 判断是否需要删除的特性, 值为空,后者函数判断
// track(26)
// track 函数
// url: src/react/packages/react-dom/src/client/inputValueTracking.js
export function track(node: ElementWithValueTracker) {
// 初始时, node._valueTracker 不存在
if (getTracker(node)) { // 如果存在数据
return;
}
// 只执行一次
node._valueTracker = trackValueOnNode(node);
}
// getTracker 函数
function getTracker(node: ElementWithValueTracker) {
return node._valueTracker; // 返回_valueTracker
}
// 获取节点的_valueTracker 属性
/// trackValueOnNode 函数
function trackValueOnNode(node: any): ?ValueTracker {
const valueField = isCheckable(node) ? 'checked' : 'value';
const descriptor = Object.getOwnPropertyDescriptor(
node.constructor.prototype,
valueField,
);
let currentValue = '' + node[valueField];
if (
node.hasOwnProperty(valueField) ||
typeof descriptor === 'undefined' ||
typeof descriptor.get !== 'function' ||
typeof descriptor.set !== 'function'
) {
return;
}
const {get, set} = descriptor;
Object.defineProperty(node, valueField, {
configurable: true,
get: function() {
return get.call(this);
},
set: function(value) {
currentValue = '' + value;
set.call(this, value);
},
});
Object.defineProperty(node, valueField, {
enumerable: descriptor.enumerable,
});
const tracker = {
getValue() {
return currentValue;
},
setValue(value) {
currentValue = '' + value;
},
stopTracking() {
detachTracker(node);
delete node[valueField];
},
};
return tracker;
}
// 备注: 返回一个tracker, 什么用途?
// ReactDOMInputPostMountWrapper(27)
// url: src/react/packages/react-dom/src/client/ReactDOMInput.js
import {
postMountWrapper as ReactDOMInputPostMountWrapper,
} from './ReactDOMInput';
export function postMountWrapper(
element: Element,
props: Object,
isHydrating: boolean,
) {
const node = ((element: any): InputWithWrapperState);
if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
const type = props.type;
const isButton = type === 'submit' || type === 'reset'; // 重置按钮或者提交
if (isButton && (props.value === undefined || props.value === null)) {
return; // 跳过
}
// 节点的初始值
const initialValue = toString(node._wrapperState.initialValue);
if (!isHydrating) { // true
if (disableInputAttributeSyncing) {
const value = getToStringValue(props.value);
if (value != null) {
if (isButton || value !== node.value) {
node.value = toString(value);
}
}
} else {
if (initialValue !== node.value) {
node.value = initialValue;
}
}
}
if (disableInputAttributeSyncing) {
const defaultValue = getToStringValue(props.defaultValue);
if (defaultValue != null) {
node.defaultValue = toString(defaultValue);
}
} else {
node.defaultValue = initialValue;
}
}
const name = node.name;
if (name !== '') {
node.name = '';
}
if (disableInputAttributeSyncing) {
if (!isHydrating) {
updateChecked(element, props);
}
if (props.hasOwnProperty('defaultChecked')) {
node.defaultChecked = !node.defaultChecked;
node.defaultChecked = !!props.defaultChecked;
}
} else {
node.defaultChecked = !node.defaultChecked;
node.defaultChecked = !!node._wrapperState.initialChecked;
}
if (name !== '') {
node.name = name;
}
}
// 备注: input类型的处理
// ReactDOMTextareaPostMountWrapper (28)
import {
postMountWrapper as ReactDOMTextareaPostMountWrapper,
} from './ReactDOMTextarea';
// url: src/react/packages/react-dom/src/client/ReactDOMTextarea.js
export function postMountWrapper(element: Element, props: Object) {
const node = ((element: any): TextAreaWithWrapperState);
const textContent = node.textContent;
if (textContent === node._wrapperState.initialValue) {
if (textContent !== '' && textContent !== null) {
node.value = textContent;
}
}
}
// 备注: 设置节点的数值?
// ReactDOMOptionPostMountWrapper(29)
import {
postMountWrapper as ReactDOMOptionPostMountWrapper,
} from './ReactDOMOption';
// url: src/react/packages/react-dom/src/client/ReactDOMOption.js
export function postMountWrapper(element: Element, props: Object) {
if (props.value != null) {
element.setAttribute('value', toString(getToStringValue(props.value)));
}
}
// 备注: 设置 option 类型的值
// ReactDOMSelectPostMountWrapper
import {
postMountWrapper as ReactDOMSelectPostMountWrapper,
} from './ReactDOMSelect';
// postMountWrapper
// url: src/react/packages/react-dom/src/client/ReactDOMSelect.js
export function postMountWrapper(element: Element, props: Object) {
const node = ((element: any): SelectWithWrapperState);
node.multiple = !!props.multiple;
const value = props.value;
if (value != null) {
updateOptions(node, !!props.multiple, value, false);
} else if (props.defaultValue != null) {
updateOptions(node, !!props.multiple, props.defaultValue, true);
}
}
// 备注: 设置options的数据
// updateOptions(30)
// 定义updateOptions
// url: src/react/packages/react-dom/src/client/ReactDOMSelect.js
function updateOptions(
node: HTMLSelectElement,
multiple: boolean,
propValue: any,
setDefaultSelected: boolean,
) {
type IndexableHTMLOptionsCollection = HTMLOptionsCollection & {
[key: number]: HTMLOptionElement,
...,
};
const options: IndexableHTMLOptionsCollection = node.options;
if (multiple) { // 可以多选
const selectedValues = (propValue: Array<string>);
const selectedValue = {};
for (let i = 0; i < selectedValues.length; i++) {
// Prefix to avoid chaos with special keys.
selectedValue['$' + selectedValues[i]] = true;
}
for (let i = 0; i < options.length; i++) {
const selected = selectedValue.hasOwnProperty('$' + options[i].value);
if (options[i].selected !== selected) {
options[i].selected = selected;
}
if (selected && setDefaultSelected) {
options[i].defaultSelected = true;
}
}
} else {
// Do not set `select.value` as exact behavior isn't consistent across all
// browsers for all cases.
const selectedValue = toString(getToStringValue((propValue: any)));
let defaultSelected = null;
for (let i = 0; i < options.length; i++) {
if (options[i].value === selectedValue) {
options[i].selected = true;
if (setDefaultSelected) {
options[i].defaultSelected = true;
}
return;
}
if (defaultSelected === null && !options[i].disabled) {
defaultSelected = options[i];
}
}
if (defaultSelected !== null) {
defaultSelected.selected = true;
}
}
}
// 备注: options数据更新
// trapClickOnNonInteractiveElement(31)
// trapClickOnNonInteractiveElement 定义
// url: src/react/packages/react-dom/src/client/ReactDOMComponent.js
export function trapClickOnNonInteractiveElement(node: HTMLElement) {
node.onclick = noop;
}
function noop() {}
// 备注: 其他类型click的执行函数, 哪里绑定函数
// shouldAutoFocusHostComponent(32)
// 确认节点是否自动焦点聚焦
// url: src/react/packages/react-dom/src/client/ReactDOMHostConfig.js
function shouldAutoFocusHostComponent(type: string, props: Props): boolean {
switch (type) {
case 'button':
case 'input':
case 'select':
case 'textarea':
return !!props.autoFocus;
}
return false;
}
// 备注: 确认当前节点是否被聚焦
// markUpdate(33)
// 当前节点有更新
// url: src/react/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
function markUpdate(workInProgress: Fiber) {
workInProgress.flags |= Update;
}
// 备注: 标示节点的更新, 在flags 上增加Update
// updateHostComponent(34)
// url: src/react/packages/react-reconciler/src/ReactFiberCompleteWork.old.js
updateHostComponent = function(
current:updateHostComponent = function(
current: Fiber,
workInProgress: Fiber,
type: Type,
newProps: Props,
rootContainerInstance: Container,
) {
const oldProps = current.memoizedProps;
// 属性没有更新
if (oldProps === newProps) { // 新旧props 没有更新
return;
}
// dom 节点
const instance: Instance = workInProgress.stateNode;
const currentHostContext = getHostContext();
// 处理更新的部分
const updatePayload = prepareUpdate( // 后取更新
instance,
type,
oldProps,
newProps,
rootContainerInstance,
currentHostContext,
);
// TODO: Type this specific to this type of component.
workInProgress.updateQueue = (updatePayload: any); // 获取更新队列
// 具有更新的时候
if (updatePayload) { // 具有更新的时候
markUpdate(workInProgress); // workInProgress.flags |= Update;
}
};
// 备注: 原生dom节点的更新, 判断props 是否相等,相等则没有更新, 如果有更新, 将
// 更新添加到updateQueue, 并将节点flags 标示Update
// prepareUpdate(35)
// url: src/react/packages/react-dom/src/client/ReactDOMHostConfig.js
export function prepareUpdate(
domElement: Instance,
type: string,
oldProps: Props,
newProps: Props,
rootContainerInstance: Container,
hostContext: HostContext,
): null | Array<mixed> {
// dev 代码, 已省略
// dom 节点的diff 算法
return diffProperties(
domElement, // dom 节点
type, // 节点
oldProps, // 旧的属性props
newProps, // 新的属性props
rootContainerInstance, // div#root
);
}
// 备注: 通过返回执行diffProperties 获取dom节点的真正更新。
// diffProperties(37)
// 计算节点的属性差异
export function diffProperties(
domElement: Element,
tag: string,
lastRawProps: Object,
nextRawProps: Object,
rootContainerElement: Element | Document,
): null | Array<mixed> {
// dev代码,已省略
// 初始值为空
let updatePayload: null | Array<any> = null; // 初始值为null
let lastProps: Object;
let nextProps: Object;
// 获取
switch (tag) {
case 'input':
lastProps = ReactDOMInputGetHostProps(domElement, lastRawProps); // 旧的props
nextProps = ReactDOMInputGetHostProps(domElement, nextRawProps);
updatePayload = []; // 设置为空数组
break;
case 'option':
lastProps = ReactDOMOptionGetHostProps(domElement, lastRawProps);
nextProps = ReactDOMOptionGetHostProps(domElement, nextRawProps);
updatePayload = []; // 设置为空数组
break;
case 'select':
lastProps = ReactDOMSelectGetHostProps(domElement, lastRawProps);
nextProps = ReactDOMSelectGetHostProps(domElement, nextRawProps);
updatePayload = []; // 设置为空数组
break;
case 'textarea':
lastProps = ReactDOMTextareaGetHostProps(domElement, lastRawProps);
nextProps = ReactDOMTextareaGetHostProps(domElement, nextRawProps);
updatePayload = []; // 设置为空数组
break;
default:
lastProps = lastRawProps;
nextProps = nextRawProps;
if (
typeof lastProps.onClick !== 'function' &&
typeof nextProps.onClick === 'function'
) {
// 空函数
trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
}
break;
}
assertValidProps(tag, nextProps); // props 校验
let propKey;
let styleName;
let styleUpdates = null; // 样式的更新
// 循环lastProps
for (propKey in lastProps) { // 遍历旧的props
// 删除的属性的遍历, 这里只记录,旧的有,新的没有的数据
if (
nextProps.hasOwnProperty(propKey) || // 新的有
!lastProps.hasOwnProperty(propKey) || // 旧的没有
lastProps[propKey] == null
) {
continue;
}
// key : style
if (propKey === STYLE) { // style
const lastStyle = lastProps[propKey];
for (styleName in lastStyle) { // 遍历旧的style
if (lastStyle.hasOwnProperty(styleName)) {
if (!styleUpdates) {
styleUpdates = {};
}
styleUpdates[styleName] = ''; // 删除的
} // style 的处理为 {styleName1: ''} // 都在一个对象
}
} else if (propKey === DANGEROUSLY_SET_INNER_HTML || propKey === CHILDREN) {
// Noop. This is handled by the clear text mechanism.
} else if (
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
propKey === SUPPRESS_HYDRATION_WARNING
) {
// Noop
} else if (propKey === AUTOFOCUS) {
// Noop. It doesn't work on updates anyway.
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
//
if (!updatePayload) {
updatePayload = [];
}
} else {
// 记录所有需要删除的属性
(updatePayload = updatePayload || []).push(propKey, null);
}
}
// 遍历新的属性
for (propKey in nextProps) {
// 属性值
const nextProp = nextProps[propKey];
//
const lastProp = lastProps != null ? lastProps[propKey] : undefined;
if (
!nextProps.hasOwnProperty(propKey) || // 原型上的
nextProp === lastProp || // 相等
(nextProp == null && lastProp == null) // 都为空
) {
continue;
}
if (propKey === STYLE) { // tyle
// dev代码,已省略
if (lastProp) { // 旧的存在
// Unset styles on `lastProp` but not on `nextProp`.
for (styleName in lastProp) {
if (
lastProp.hasOwnProperty(styleName) &&
(!nextProp || !nextProp.hasOwnProperty(styleName))
) {
// 初始化
if (!styleUpdates) {
styleUpdates = {};
}
styleUpdates[styleName] = '';
}
}
// Update styles that changed since `lastProp`.
for (styleName in nextProp) {
if (
nextProp.hasOwnProperty(styleName) &&
lastProp[styleName] !== nextProp[styleName]
) {
if (!styleUpdates) {
styleUpdates = {};
}
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
// 旧的不存在
// Relies on `updateStylesByID` not mutating `styleUpdates`.
if (!styleUpdates) { // 旧的style 没有
if (!updatePayload) {
updatePayload = [];
}
updatePayload.push(propKey, styleUpdates); //
}
styleUpdates = nextProp; // 属性更新
}
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
// 属性是否变化
const nextHtml = nextProp ? nextProp[HTML] : undefined;
const lastHtml = lastProp ? lastProp[HTML] : undefined;
if (nextHtml != null) {
if (lastHtml !== nextHtml) {
(updatePayload = updatePayload || []).push(propKey, nextHtml);
}
} else {
// TODO: It might be too late to clear this if we have children
// inserted already.
}
} else if (propKey === CHILDREN) {
// children
if (typeof nextProp === 'string' || typeof nextProp === 'number') {
(updatePayload = updatePayload || []).push(propKey, '' + nextProp);
}
} else if (
propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
propKey === SUPPRESS_HYDRATION_WARNING
) {
// Noop
} else if (registrationNameDependencies.hasOwnProperty(propKey)) {
if (nextProp != null) {
// dev代码,已省略
if (!enableEagerRootListeners) {
ensureListeningTo(rootContainerElement, propKey, domElement);
} else if (propKey === 'onScroll') {
listenToNonDelegatedEvent('scroll', domElement);
}
}
if (!updatePayload && lastProp !== nextProp) {
updatePayload = [];
}
} else if (
typeof nextProp === 'object' &&
nextProp !== null &&
nextProp.$$typeof === REACT_OPAQUE_ID_TYPE
) {
nextProp.toString();
} else {
// 新增的
(updatePayload = updatePayload || []).push(propKey, nextProp);
}
}
if (styleUpdates) {
if (__DEV__) {
validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE]);
}
(updatePayload = updatePayload || []).push(STYLE, styleUpdates);
}
return updatePayload;
}
// dom 节点的属性diff