根本上说,JSX是React提供给:React.createElement(component, props, …children)函数的语法糖。

JSX代码:

  1. <MyButton color="blue" shadowSize={2}>
  2. Click Me
  3. </MyButton>

将被编译成:

  1. React.createElement(
  2. MyButton,
  3. {color: 'blue', shadowSize: 2},
  4. 'Click Me'
  5. )

没有子元素的话,你也可以使用自闭和标签,所以:

  1. <div className="sidebar" />

编译成:

  1. React.createElement(
  2. 'div',
  3. {className: 'sidebar'},
  4. null
  5. )

你要是想测试测试这些明确的JSX被转换成怎样的javascript,可以试试 the online Babel compiler

明晰React元素类型

JSX标签的第一部分决定React元素的类型。
大写(首字母)的类型表示JSX标签是React组件。这些标签直接编译成该标签名的变量,所以,要是你的JSX是,那么其实在作用域中有Foo这个变量。

React 必须在作用域中

既然JSX编译调用的React.createElement,那么React库一定要总是在你JSX代码的作用域中。
举例,下面所有的import都是必要的,尽管React和CustomButton没有直接使用在JavaScript代码中。

  1. import React from 'react';
  2. import CustomButton from './CustomButton';
  3. function WarningButton() {
  4. // return React.createElement(CustomButton, {color: 'red'}, null);
  5. return <CustomButton color="red" />;
  6. }

如果你不用js打包器,而是用script标签引用React,那么React是存在全局域中的。

在JSX中使用’.’语法

你也可以通过点语法在JSX中来引用React组件。如果你只有一个模块,但却输出了很多组件,这样会很方便,比方说MyComponents.DatePicker 是一个组件,那么你就可以这样使用它:

  1. import React from 'react';
  2. const MyComponents = {
  3. DatePicker: function DatePicker(props) {
  4. return <div>Imagine a {props.color} datepicker here.</div>;
  5. }
  6. }
  7. function BlueDatePicker() {
  8. return <MyComponents.DatePicker color="blue" />;
  9. }

自定义的组件一定要首字母大写

当元素开头是小写字母,它表示div或span这种原生html标签,同时,编译时向React.createElement()中传递的是字符串。但像首字母大写的类型,像 ,那么传递到React.createElement()的是与定义或导入文件一致的组件变量引用。(就像开篇的例子中那样)

我们推荐使用首字母大写命名,如果你要是此前有小写开头的组件,那么在使用JSX之前先把他们变成首字母大写的。

下面的代码不会按预期运行的:

  1. import React from 'react';
  2. // Wrong! This is a component and should have been capitalized:
  3. // 错!应该用首字母大写的方式定义组件
  4. function hello(props) {
  5. // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  6. return <div>Hello {props.toWhat}</div>;
  7. }
  8. function HelloWorld() {
  9. // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
  10. // 错!React认为,hello是个原生html标签
  11. return <hello toWhat="World" />;
  12. }

来把hello变成Hello修复它:

  1. import React from 'react';
  2. // Correct! This is a component and should be capitalized:
  3. function Hello(props) {
  4. // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  5. return <div>Hello {props.toWhat}</div>;
  6. }
  7. function HelloWorld() {
  8. // Correct! React knows <Hello /> is a component because it's capitalized.
  9. return <Hello toWhat="World" />;
  10. }

运行时选择类型

你不能将通用表达式作为元素类型。如果你需要使用一个通用表达式来指明元素类型,那么先将他们分配给一个首字母大写的变量。这通常在根据props来渲染不同组件时用到。

  1. import React from 'react';
  2. import { PhotoStory, VideoStory } from './stories';
  3. const components = {
  4. photo: PhotoStory,
  5. video: VideoStory
  6. };
  7. function Story(props) {
  8. // Wrong! JSX type can't be an expression.
  9. return <components[props.storyType] story={props.story} />;
  10. }

修复这个错误,我们先分配类型给一个首字母大写的变量:

  1. import React from 'react';
  2. import { PhotoStory, VideoStory } from './stories';
  3. const components = {
  4. photo: PhotoStory,
  5. video: VideoStory
  6. };
  7. function Story(props) {
  8. // Correct! JSX type can be a capitalized variable.
  9. const SpecificStory = components[props.storyType];
  10. return <SpecificStory story={props.story} />;
  11. }

JSX中的props

JSX中分配props有几种不同的方式:

JavaScript表达式作为props值

使用一对花括号{},你就能在JSX中把js表达式作为props传入,举例:

  1. <MyComponent foo={1 + 2 + 3 + 4} />

对于MyComponent,props.foo的值是10,因为1+2+3+4已经计算过了。

if和for不算表达式,算语句,所以,不能出现在JSX中。取而代之,你可以将他们放在附近代码中,就像:

  1. function NumberDescriber(props) {
  2. let description;
  3. if (props.number % 2 == 0) {
  4. description = <strong>even</strong>;
  5. } else {
  6. description = <i>odd</i>;
  7. }
  8. return <div>{props.number} is an {description} number</div>;
  9. }

在前面的相关章节有更多关于“条件渲染”的知识。QUICK START, Conditional Rendering

字符串字面值

你可以直接在属性中传递字符串,下面这两个写法没区别:

  1. <MyComponent message="hello world" />
  2. <MyComponent message={'hello world'} />

在你传递字符串字面值的时候,他的值就是HTML-unescaped(HTML保有值),所以下面两种也是相等的:

  1. <MyComponent message="&lt;3" />
  2. <MyComponent message={'<3'} />

这个问题关系不大,这里提及只是为了知识完整性。

Props属性的True默认值

你要是传递了一个“没有值”的props属性,它的默认值就是true,下面两种写法相等:

  1. <MyTextBox autocomplete />
  2. <MyTextBox autocomplete={true} />

一般来说我们不推荐这样使用,因为这样容易和ES6 Obeject Shorthand特性相冲突,在Shorthand特性中{foo}写法表示{foo:foo}而不是{foo:true}。这里的这种行为只是为了对应HTML的行为。

展开属性

如果你已经将需要传递的props写成了一个对象,那么你也许需要使用…运算符,展开运算符,来讲整个props对象传递,下面两种写法等价。

  1. function App1() {
  2. return <Greeting firstName="Ben" lastName="Hector" />;
  3. }
  4. function App2() {
  5. const props = {firstName: 'Ben', lastName: 'Hector'};
  6. return <Greeting {...props} />;
  7. }

你也可以调出那些将要在组件中处理掉的属性同时使用…传递其他属性。

  1. const Button = props => {
  2. const { kind, ...other } = props;
  3. const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
  4. return <button className={className} {...other} />;
  5. };
  6. const App = () => {
  7. return (
  8. <div>
  9. <Button kind="primary" onClick={() => console.log("clicked!")}>
  10. Hello World!
  11. </Button>
  12. </div>
  13. );
  14. };

上面的例子中,kind,props属性安全的“被消费”在传递进button元素之前,剩下的所有props通过…other对象传递,你可以看到,onClick和children都被传递下去了。
扩展运算符简单好用,但是很容易把不必要的props或者不可用的html属性传递到组件中,我们推荐有节制地使用这样的语法。

JSX中的子元素

JSX表达式包含开放和闭合标签,标签间的内容是依靠一个特殊的属性传递:props.children。有几种方式来传递子元素:

字符串值

你可以将string放置在标签中,这时候props.children的值就是string本身。对于许多基于html元素来讲,这是有用的。
比如:

  1. <MyComponent>Hello world!</MyComponent>

这就是一个可用的JSX,props.children的值就是“Hello World!”。Html值就是HTML-unescaped(HTML保有值),所以你也可以像写html那样写jsx:

  1. <div>This is valid HTML &amp; JSX at the same time.</div>

JSX会移除行首尾的空格,也会移除空行,标签临近的会被移除,字符串中间的新行会被压缩成一个空格。所以一下几种情况渲染的东西都一样:

  1. <div>Hello World</div>
  2. <div>
  3. Hello World
  4. </div>
  5. <div>
  6. Hello
  7. World
  8. </div>
  9. <div>
  10. Hello World
  11. </div>

JSX子元素

你可以提供更多JSX元素作为子元素,这对于展示嵌套的组件是有效的:

  1. <MyContainer>
  2. <MyFirstComponent />
  3. <MySecondComponent />
  4. </MyContainer>

你也可以混合不同类型的子元素,所以,你可以将字符串和JSX子元素混合用。这是JSX和html另一点相像之处,所以,下面的既能JSX中使用,也能HTML中使用:

  1. <div>
  2. Here is a list:
  3. <ul>
  4. <li>Item 1</li>
  5. <li>Item 2</li>
  6. </ul>
  7. </div>

React组件也能返回医嘱对象,对象数组:

  1. render() {
  2. // No need to wrap list items in an extra element!
  3. return [
  4. // Don't forget the keys :)
  5. <li key="A">First item</li>,
  6. <li key="B">Second item</li>,
  7. <li key="C">Third item</li>,
  8. ];
  9. }

JS表达式作为子元素

任何js表达式都能在一放在一对花括号中,举例来说,下面的表达式等价:

  1. <MyComponent>foo</MyComponent>
  2. <MyComponent>{'foo'}</MyComponent>

这种通常在渲染一个任意长度JSX列表时格外有用,比如,下面渲染了一个HTML的列表:

  1. function Item(props) {
  2. return <li>{props.message}</li>;
  3. }
  4. function TodoList() {
  5. const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  6. return (
  7. <ul>
  8. {todos.map((message) => <Item key={message} message={message} />)}
  9. </ul>
  10. );
  11. }

js表达式也可以混合其他类型的子元素写法,通常,代替字符串模板时非常有用。

  1. function Hello(props) {
  2. return <div>Hello {props.addressee}!</div>;
  3. }

函数作为子元素

通常地,JSX中的JavaScript表达式的值作为字符串,React元素或者这些东西的列表。然而,props.children和其他props一样的,当你传递其他数据序列集合不仅仅是那些react知道如何渲染的集合时也能工作。比如,假设你有一个自定义组件,你可以让它持有一个回调函数作为props.children。

  1. // Calls the children callback numTimes to produce a repeated component
  2. function Repeat(props) {
  3. let items = [];
  4. for (let i = 0; i < props.numTimes; i++) {
  5. // 这里回调了
  6. items.push(props.children(i));
  7. }
  8. return <div>{items}</div>;
  9. }
  10. function ListOfTenThings() {
  11. return (
  12. <Repeat numTimes={10}>
  13. {(index) => <div key={index}>This is item {index} in the list</div>}
  14. </Repeat>
  15. );
  16. }
  17. //这个最终渲染的结果为:
  18. This is item 0 in the list
  19. This is item 1 in the list
  20. This is item 2 in the list
  21. This is item 3 in the list
  22. This is item 4 in the list
  23. This is item 5 in the list
  24. This is item 6 in the list
  25. This is item 7 in the list
  26. This is item 8 in the list
  27. This is item 9 in the list

向组件中添加的子组件可以是任何东西,只要React能够在渲染之前将他们转换成React能理解的东西。这一点不常用,不过如果你想对jsx的能力来点延伸时,它是可用的。

Booleans, Null, 以及 Undefined 将被忽略

false, null, udefined和true是可用的子组件,他们不会被渲染,下面这些jsx会被渲染成相同的东西:

  1. <div />
  2. <div></div>
  3. <div>{false}</div>
  4. <div>{null}</div>
  5. <div>{undefined}</div>
  6. <div>{true}</div>

这一点倒是在条件渲染中可用。这个JSX会在showHeader是True的时候渲染出Header组件。

  1. <div>
  2. {showHeader && <Header />}
  3. <Content />
  4. </div>

这里有个附加警告:一些falsy值(类似false的值),比如0,依旧会被渲染。举例,下面的代码并不会如你期望那样,因为0间如果props.message是空的,0也将会被打印出来。

  1. <div>
  2. {props.messages.length &&
  3. <MessageList messages={props.messages} />
  4. }
  5. </div>

修复它的话就要保证你的表达式始终是一个布尔表达式:

  1. <div>
  2. {props.messages.length > 0 &&
  3. <MessageList messages={props.messages} />
  4. }
  5. </div>

反过来说,你要是想要这种false,true,null,undefined出现在输出中,你得用string()函数转换他们先:

  1. <div>
  2. My JavaScript variable is {String(myVariable)}.
  3. </div>

官网文章 Advanced Guides :JSX In Depthe