可能你写过相当长一段时间的React,都没有用过甚至是听说过React.createElement这个API。但实际上你写的每个Component都使用了React.createElement。我们通常会使用React.createElement来写React组件,JSX为JavaScript增加了类似于HTML标签的写法,再经过babel编译后,会生成常规的JavaScript文件。

React.createElement的函数签名为:

function createElement(type, props, ...children)

JSX

JSX是对JavaScript的一个语法拓展,为[JavaScript Primary Expression]增加了JSXElement和JSXFragment两个变体。JSX的语法标准可见Draft: JSX Specification

我们主要通过babel和JSX打交道,可以在babel的在线repl中探索jsx语法的标签是怎样被编译成js的。

标签的命名

一个简单的原生标签:

<div />
// =>
React.createElement("div", null);

自定义标签:

<Greeting />
// => 
React.createElement(Greeting, null);

可以看到,对于原生标签,React使用string作为type,而对于自定义标签,React会传入function或者class的值作为type。

对于babel来说,如果标签名是一个不包含unicode字符,且以非小写字母开头的JavaScript标识符,也即满足[$_A-Z][$a-zA-Z0-9_]*的正则,都认为是自定义标签:

<Foo />
<FOO />
<$Foo />
<_foo />
<$ />

一个特例是,自定义标签可以包含JavaScript的property access语法,也即obj.prop.deeperProp,但不能是obj["prop"]obj[propKey]。这在将component作为props传入时很有用:

<this.props.FallbackComponent />

对与原生标签,babel并没有为其维护一个类型,而是把任何满足一下命名规则的标签都作为原生标签:

  1. 组成标签的字符集为[$a-zA-Z0-9_\-],也即比合法的JavaScript的标识符字符集多了个-
  2. 以小写字母开头,或者标签名包含-
  3. 不以-开头。

以下标签都被认为是原生标签:

<foo />
<fOO />
<Foo- />
<$-_ />

对于ReactDOM来说, 一个空标签的type为React.Fragment

<> </>
// => 
React.createElement(React.Fragment, null);

空标签不允许自闭合。也即:</>不是一个合法的jsx表达式。这很容易理解,React Fragment在设计上就是被用来包含子标签的,以props的形式传递children显然也不是一个行得通的写法:< children={<div />} />

Props

一个jsx标签内可以包含0个或多个props。props是一组键值对,形如:

<foo key1='val1' key2={val2} key3={<val3/>} key4 />

以上代码会被babel编译成:

React.createElement("foo", {
  key1: "val1",  							// 1
  key2: val2,								// 2
  key3: React.createElement("val3", null)
  key4: true,								// 3
});

一些说明:

  1. props的值可以为合法的HTML string literal,也即"val"或者'val',但不能是`val`
  2. props的值可以是由{}包围的任意的JavaScript或jsx表达式。一个简单的判据是:{}中的内容能不经修改地作为函数的参数而不引起语法错误。 以下都是合法的JavaScript表达式
    1. a
    2. true
    3. a === b
    4. a > b ? c : d
    5. a.b
    6. arr.map((str) => <span>{str}</span>)
    7. a = b (表达式的值为b) 以下不是合法的JavaScript表达式
    8. if (cond) expr
    9. expr;
    10. return true
  3. 当省略props的值时,等同于传给props的值为true。