先讲一点react的基础部分:

1. react中创建组件的写法:

var a = <Component {...props} />,实质上是React调用createElement方法,生成一个虚拟的DOM元素。等同于如下:
var a = React.createElement(Component, props);

2. 创建类的写法:

class Component{ },其本质是一个方法, 在babel中转化如下:

1
2
3
4
5
6
7
8
9
 function _classCallCheck(instance, Constructor) { 
if (!(instance instanceof Constructor)) { // 类型检测
throw new TypeError("Cannot call a class as a function");
}
}

var Component = function Component() {
_classCallCheck(this, Component);
};

3. 在DOM中渲染一个button按钮:

1
2
3
var child = React.createElement('p', { id: 'bbb' }, 'click me');
var app = React.createElement('button', { id: 'aaa', onClick: () => console.log(1111) }, child);
ReactDOM.render(app, document.getElementById('root'));

上述将生成一个包裹着p标签的button组件。ReactElement: 一个描述DOM节点或component实例的字面级对象。创建并返回一个新的react element元素及虚拟DOM对象。type可以是一个普通的标签名如div或span, 亦可以是react组件类型(class或是function)。

4. 提供一个改变组件名字的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ButtonForTest extends Component {  // 写一个class供addName方法使用,也可以是一个function
render() {
return (
<Button>1111</Button>
)
}
}
function addName(name, Component) { // 接收一个名字,及一个组件
function WithName(props) {
return <Component {...props} />;
}
WithName.displayName = `${name}`; // 改变function 的名字,即改变WithName类的名字。
return WithName;
}
const newName = { key: addName('NewName', ButtonForTest) };
const NewComponent = <newName.key />; // -》 var c = React.createElement(newName.key, null);

上诉NewComponent所得为NewName组件下包含着ButtonForTest组件。

笔者一直感到奇怪,NewComponent获得的是一个带有display属性的function,并不是一个class,为什么也可以渲染出来呢。

其实上面已经说到了,类的本质就是一个方法。 <newName.key />转化为js的写法是 React.createElement(newName.key, null),createElement接收三个参数(type, config, children), type属性为字符串时,它代表是普通的节点,如div,span。type属性为一个函数或一个类时,它代表自定义的节点。因此上述方法即是获得一个addName方法返回的组件,其中对所要包裹的组件,(即第二个参数)外部包裹一层名为NewName 的组件。

5. 简单看一下react中createElement部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
ReactElement.createElement = function(type, config, children) {
var propName;

// Reserved names are extracted
var props = {};

var key = null;
var ref = null;
var self = null;
var source = null;

if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
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
// 将config里的属性复制到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.
//处理children,全部挂载到props的children属性上
//支持两种写法,如果只有一个参数,直接赋值给children,否则做合并处理
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}

// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
if (
typeof props.$$typeof === 'undefined' ||
props.$$typeof !== REACT_ELEMENT_TYPE
) {
var displayName = typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
}
return ReactElement( // 返回ReactElement对象供React使用。
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
};

官方有更详尽的讲解:https://facebook.github.io/react/docs/react-api.html#createelement