JSX 最简单的语法的可能是这样的:

const greetWord = <span>HI</span>;

可以看到,它既然不是字符串,也不是变量。而是一个不带双引号的、HTML标签与文本内容混合在一起的格式。

要想在页面显示上面的内容,我们必须把它放入一个组件中。首先,定义一个组件,再利用 {} 符号,把上面的 jsx 内容插入组件中:

import React from 'react';
import ReactDOM from 'react-dom';

class Greet extends Component {
render () {
return <div>{greetWord}</div>;
}
};

ReactDOM.render(
<Greet name="yix"/>,
document.getElementById('root)
)

当然,这里的 <div>{greetWord}</div> 也可以替换成 greetWord,只需要保证 return 的内容是被HTML标签包裹的即可。这样,Greet 组件

或者,你无需在前面定义 jsx,即可以直接在组件里插入内容:

class Greet extends Component {
render () {
return <div><span>HI</span></div>
}
};

为了代码的可读性,当 return 的内容涉及多行时,我们会把分成多行:

class Greet extends Component {
render () {
return
<div>
<span>HI</span>
</div>

}
};

但运行时,却发现报错,提示返回的是非法格式。由于js中 return 后面会自动补 ;,所以这里返回的是 undefined。我们尝试改进:

class Greet extends Component {
render () {
return <div>
<span>HI</span>
</div>

}
};

在 return 后面紧跟着 <div>,这样就能正常渲染组件了。不过,官方则推荐使用 () 包裹 return 的内容,即:

class Greet extends Component {
render () {
return (
<div>
<span>HI</span>
</div>

)
}
};

组件中 {} 里的内容非常灵活,可以是任意的JavaScript表达式,变量、函数、运算都可以,如:

function inputName() {
return 'yix';
}

class Greet extends Component {
render () {
return (
<div>
<span>HI</span>
<h1>{inputName()}</h1>
<p>现在是:{(new Date()).toString()}</p>
</div>

)
}
};

渲染组件

前面主要说组件的创建,而要渲染这个组件,则需要引入 react-dom 库,如:

import React from 'react';
import ReactDOM from 'react-dom';

function inputName() {
return 'yix';
}

class Greet extends Component {
render () {
return (
<div>
<span>HI</span>
<h1>{inputName()}</h1>
</div>

)
}
};

ReactDOM.render(
<Greet />,
document.getElementById('root)
)

这样,组件 Greet 便在 id 为 root 的节点下进行了渲染。

有的时候,我们需要针对条件来隐藏(不渲染)组件,你只需要 return null 即可。比如 LoadingTips 加载组件,当父组件的ajax请求完毕后,可根据传递的 props.load 属性,来决定 LoadingTips 组件的渲染与否:

function LoadingTips(props) {
if (this.props.load) {
return null;
}

return (
<div className="loading-layer">
loading...
</div>

)
}

创建组件

目前看来,react组件的创建主要分为三大类,即:

  • 纯函数创建无状态组件-SFC
  • ES5-React.createClass
  • ES6-extends React.Component

SFC

SFC,即Stateless Functional Component,也被叫做 无状态功能组件,比如,我们文章开头提到的组件,就是采用这种形式编写的。

SFC组件通常被用来单纯展示一些UI界面,这种组件的数据来源主要是通过 props,不会涉及任何 state 的操作,所以被称为 无状态组件。可通过如下语法创建:

var React = require('react');
var ReactDOM = require('react-dom');

function InputName(props) {
return <h1>{props.name}</h1>;
}

ReactDOM.render(
<InputName name="yix" />,
document.getElementById('root)
)

在上面的代码中,InputName 就是一个 SFC组件。

作为纯展示的组件,SFC组件的特点是不会被实例化,性能大幅度提升。因为没有状态,不能使用 this.state 来访问状态数据。只能通过 this.props 访问父级传递的数据,并且照搬显示该 this.props,因此,不会有副作用。

介于代码简洁、性能高效以及无副作用,所以,提倡在大型应用中,尽可能把组件分割成无状态组件。

React.createClass

ES5创建组件的方式则是采用如下语法:

const React = require('react');
const ReactDOM = require('react-dom');

const Greet = React.createClass({

propTypes: { // 类型检测
name: React.PropTypes.name.isRequired
}

getDefaultProps: function() { // 默认属性
return {
phone: '123'
}
},

getInitialState: function() { // 初始state
return {
name: 'xx'
}
},

changeName: function() {
this.setState({name: 'yix'});
},

render: function() {
return (
<div>
<h1>{this.state.name}</h1>
<button onClick={this.changeName}></button>
</div>

)
}
});

这里可以看到,组件 propsstate 的相关方法都定义在组件内部。并且因为存在实例化过程,我们也可以通过 this.propsthis.state 来访问组件的数据。

相应地,我们还可以在组件内添加生命周期方法,并进行相关处理。

另外,对于使用 React.createClass 这种形式创建的组件,react做了内部处理。组件里面的所有函数都会自动进行this绑定。除此之外,这种形式的组件,还支持 mixin 功能。

extends React.Component

要说目前最流行的创建react组件语法,当然是ES6类结合继承的形式:

import React, {Component} from 'react'
import {render} from 'react-dom'

class Greet extends Component {
constructor(props) {
super(props);

this.state = { // 初始state
text: 'xx'
};

this.changeName = this.changeName.bind(this);
}

changeName(event) {
this.setState({name: 'yix'});
}

render() {
return (
<div>
<h1>{this.state.name}</h1>
<button onClick={this.changeName}></button>
</div>

);
}
}

Greeting.propTypes = { // 类型检测
name: React.PropTypes.string.isRequired
}

Greet.defaultProps = { // 默认属性
phone: '123'
}

extends React.Component 这种形式创建的组件,把 类型检测 和 默认属性 “外置” 了,因为 ES6 的子类一开始没有 this 对象,通过在子类的构造函数中调用 super 方法来获取父级的引用。

而在里面传入 props 参数,则表明可在 constructor 构造函数中通过 this.props 来访问父组件挂载的属性。但倘若你是在 render() 函数里访问 this.props,则 super 方法无需传入 props 参数:

class Greet extends Component {
constructor() {
super();

...
}

...

render() {

console.log(this.props); // 正常访问

return (
...
);
}
}

构造函数里除了 super 方法,我们还需要在 this 上设置了初始的 state 属性。

但是,这种形式的组件有个缺点。由于我们是点 button 按钮,则事件内部的 this 实际上是 button DOM。因此,这种形式创建的组件,其中的的事件函数句柄不会自动绑定当前实例的 this,所以,我们还得在构造函数里手动绑定 this。

另外,它还不支持react的 mixin 功能。但可以考虑用封装高阶组件进行代替。