本文主要學習React.createElement()API的應用。在學習以前,咱們須要知道React的幾個基礎核心概念,React Element,React Component,React Instance不熟悉的童鞋推薦閱讀下文。javascript
React Components, Elements, and Instances。html
咱們都知道JSX是React中的一種標籤語法,它是一個語法糖,主要用來取代React.createElement()產生React Element。可是瀏覽器呢不支持JSX,所以咱們須要一個工具Babel來進行翻譯。所以Babel就是把JSX轉爲瀏覽器相兼容的格式。java
咱們能夠去Babel官網提供的環境寫幾個DEMO看看它是怎麼轉換的。源碼位置在createelement。react
// ReactSymbols.js
// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
export const REACT_ELEMENT_TYPE = hasSymbol
? Symbol.for('react.element')
: 0xeac7;
複製代碼
// ReactElement.js
/** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement * type: 節點類型,能夠是原生DOM節點或者React Component。React Component的type須要大寫,不然看成普通DOM處理。 * config: 對應的屬性集合。 * children: 子節點。 */
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
// 找出對應ref
if (hasValidRef(config)) {
ref = config.ref;
}
// 找出對應key
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
// 遍歷取出propName掛載props
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
// 子節點長度
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
// 找出對應全部子節點children, 轉爲數組掛載props
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
// defaultProps 能夠爲 Class 組件添加默認 props。這通常用於 props 未賦值,但又不能爲 null 的狀況
// 爲props添加defaultProps
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
// 若是 props.xxx 被設置爲 null,則它將保持爲 null
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
/** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, no instanceof check * will work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @internal */
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
// 經過Symbol做爲獨一無二標識符 Symbol.for進行復用
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
// 節點類型
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner,
};
if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};
複製代碼
咱們將下面代碼在React中執行看看打印出來的React Element是什麼樣子。api
function Comp() {
return React.createElement("button", null, "btn");
}
const a = React.createElement("Comp", {
className: "demo",
attr: "12"
}, "inner", React.createElement("p", {
id: "p"
}, "p"), React.createElement("span", {
id: "span"
}, "span"));
console.log(a)
複製代碼
咱們能夠對比下真正打出的React Element。 數組
如今對React.createElemt有一個粗淺的認識,至於如何將其轉爲爲真正的DOM日後看。