可能你写过相当长一段时间的React,都没有用过甚至是听说过React.createElement
这个API。但实际上你写的每个Component都使用了React.createElement
。我们通常会使用React.createElement来写React组件,JSX为JavaScript增加了类似于HTML标签的写法,再经过babel编译后,会生成常规的JavaScript文件。
React.createElement的函数签名为:
JSX
JSX是对JavaScript的一个语法拓展,为[JavaScript Primary Expression]增加了JSXElement和JSXFragment两个变体。JSX的语法标准可见Draft: JSX Specification。
我们主要通过babel和JSX打交道,可以在babel的在线repl中探索jsx语法的标签是怎样被编译成js的。
标签的命名
一个简单的原生标签:
自定义标签:
可以看到,对于原生标签,React使用string作为type,而对于自定义标签,React会传入function或者class的值作为type。
对于babel来说,如果标签名是一个不包含unicode字符,且以非小写字母开头的JavaScript标识符,也即满足[$_A-Z][$a-zA-Z0-9_]*
的正则,都认为是自定义标签:
一个特例是,自定义标签可以包含JavaScript的property access语法,也即obj.prop.deeperProp
,但不能是obj["prop"]
或obj[propKey]
。这在将component作为props传入时很有用:
对与原生标签,babel并没有为其维护一个类型,而是把任何满足一下命名规则的标签都作为原生标签:
- 组成标签的字符集为
[$a-zA-Z0-9_\-]
,也即比合法的JavaScript的标识符字符集多了个-
。 - 以小写字母开头,或者标签名包含
-
。 - 不以
-
开头。
以下标签都被认为是原生标签:
对于ReactDOM来说, 一个空标签的type为React.Fragment:
空标签不允许自闭合。也即:</>
不是一个合法的jsx表达式。这很容易理解,React Fragment在设计上就是被用来包含子标签的,以props的形式传递children显然也不是一个行得通的写法:< children={<div />} />
。
Props
一个jsx标签内可以包含0个或多个props。props是一组键值对,形如:
以上代码会被babel编译成:
一些说明:
- props的值可以为合法的HTML string literal,也即
"val"
或者'val'
,但不能是`val` - props的值可以是由
{}
包围的任意的JavaScript或jsx表达式。一个简单的判据是:{}
中的内容能不经修改地作为函数的参数而不引起语法错误。 以下都是合法的JavaScript表达式a
true
a === b
a > b ? c : d
a.b
arr.map((str) => <span>{str}</span>)
a = b
(表达式的值为b) 以下不是合法的JavaScript表达式if (cond) expr
expr;
return true
- 当省略props的值时,等同于传给props的值为true。