packages/core/createElement.js
import {
typeNumber,
toWarnDev,
hasSymbol,
REACT_ELEMENT_TYPE,
hasOwnProperty
} from './util';
import { Renderer } from './createRenderer';
import { Component } from './Component';
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true
};
function makeProps(type, config, props, children, len) {
// Remaining properties override existing props
let defaultProps, propName;
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
if (type && type.defaultProps) {
defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (len === 1) {
props.children = children[0];
} else if (len > 1) {
props.children = children;
}
return props;
}
function hasValidRef(config) {
return config.ref !== undefined;
}
function hasValidKey(config) {
return config.key !== undefined;
}
/**
* 虚拟DOM工厂
*
* @param {string|function|Component} type
* @param {object} props
* @param {array} ...children
* @returns
*/
export function createElement(type, config, ...children) {
let props = {},
tag = 5,
key = null,
ref = null,
argsLen = children.length;
if (type && type.call) {
tag = type.prototype && type.prototype.render ? 2 : 1;
} else if (type + '' !== type) {
toWarnDev('React.createElement: type is invalid.');
}
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
}
props = makeProps(type, config || {}, props, children, argsLen);
return ReactElement(type, tag, props, key, ref, Renderer.currentOwner);
}
export function cloneElement(element, config, ...children) {
// Original props are copied
let props = Object.assign({}, element.props);
// Reserved names are extracted
let type = element.type;
let key = element.key;
let ref = element.ref;
let tag = element.tag;
// Owner will be preserved, unless ref is overridden
let owner = element._owner;
let argsLen = children.length;
if (config != null) {
if (hasValidRef(config)) {
// Silently steal the ref from the parent.
ref = config.ref;
owner = Renderer.currentOwner;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
}
props = makeProps(type, config || {}, props, children, argsLen);
return ReactElement(type, tag, props, key, ref, owner);
}
export function createFactory(type) {
// console.warn('createFactory is deprecated');
var factory = createElement.bind(null, type);
factory.type = type;
return factory;
}
/*
tag的值
FunctionComponent = 1;
ClassComponent = 2;
HostPortal = 4;
HostComponent = 5;
HostText = 6;
Fragment = 7;
*/
function ReactElement(type, tag, props, key, ref, owner) {
var ret = {
type,
tag,
props
};
if (tag !== 6) {
ret.$$typeof = REACT_ELEMENT_TYPE;
ret.key = key || null;
let refType = typeNumber(ref);
if (
refType === 2 ||
refType === 3 ||
refType === 4 ||
refType === 5 ||
refType === 8
) {
//boolean number, string, function,object
if (refType < 4) {
ref += '';
}
ret.ref = ref;
} else {
ret.ref = null;
}
ret._owner = owner;
}
return ret;
}
export function isValidElement(vnode) {
return !!vnode && vnode.$$typeof === REACT_ELEMENT_TYPE;
}
export function createVText(text) {
return ReactElement('#text', 6, text + '');
}
function escape(key) {
const escapeRegex = /[=:]/g;
const escaperLookup = {
'=': '=0',
':': '=2'
};
const escapedString = ('' + key).replace(escapeRegex, function(match) {
return escaperLookup[match];
});
return '$' + escapedString;
}
let lastText, flattenIndex, flattenObject;
function flattenCb(context, child, key, childType) {
if (child === null) {
lastText = null;
return;
}
if (childType === 3 || childType === 4) {
if (lastText) {
lastText.props += child;
return;
}
lastText = child = createVText(child);
} else {
lastText = null;
}
if (!flattenObject[key]) {
flattenObject[key] = child;
} else {
key = '.' + flattenIndex;
flattenObject[key] = child;
}
flattenIndex++;
}
export function fiberizeChildren(children, fiber) {
flattenObject = {};
flattenIndex = 0;
if (children !== void 666) {
lastText = null; //c 为fiber.props.children
traverseAllChildren(children, '', flattenCb);
}
flattenIndex = 0;
return (fiber.children = flattenObject);
}
function getComponentKey(component, index) {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
if (Object(component).key != null ) {
// Explicit key
return escape(component.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
}
const SEPARATOR = '.';
const SUBSEPARATOR = ':';
//operateChildren有着复杂的逻辑,如果第一层是可遍历对象,那么
export function traverseAllChildren(
children,
nameSoFar,
callback,
bookKeeping
) {
let childType = typeNumber(children);
let invokeCallback = false;
switch (childType) {
case 0: //undefined
case 1: //null
case 2: //boolean
case 5: //function
case 6: //symbol
children = null;
invokeCallback = true;
break;
case 3: //string
case 4: //number
invokeCallback = true;
break;
// 7 array
case 8: //object
if (children.$$typeof || children instanceof Component) {
invokeCallback = true;
} else if (children.hasOwnProperty('toString')) {
children = children + '';
invokeCallback = true;
childType = 3;
}
break;
}
if (invokeCallback) {
callback(
bookKeeping,
children,
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
nameSoFar === ''
? SEPARATOR + getComponentKey(children, 0)
: nameSoFar,
childType
);
return 1;
}
let subtreeCount = 0; // Count of children found in the current subtree.
const nextNamePrefix =
nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
if (children.forEach) {
//数组,Map, Set
children.forEach(function(child, i) {
let nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseAllChildren(
child,
nextName,
callback,
bookKeeping
);
});
return subtreeCount;
}
const iteratorFn = getIteractor(children);
if (iteratorFn) {
let iterator = iteratorFn.call(children),
child,
ii = 0,
step,
nextName;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseAllChildren(
child,
nextName,
callback,
bookKeeping
);
}
return subtreeCount;
}
throw 'children: type is invalid.';
}
let REAL_SYMBOL = hasSymbol && Symbol.iterator;
let FAKE_SYMBOL = '@@iterator';
function getIteractor(a) {
let iteratorFn = (REAL_SYMBOL && a[REAL_SYMBOL]) || a[FAKE_SYMBOL];
if (iteratorFn && iteratorFn.call) {
return iteratorFn;
}
}