React 专一于 view 层,组件化则是 React 的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成。react
截至目前 React 已经更新到 v15.4.2,因为 ES6 的普及和不一样业务场景的影响,咱们会发现目前主要有三种建立 React 组件的写法:1. ES5写法React.createClass,2. ES6写法React.Component,3. 无状态的函数式写法(纯组件-SFC)。git
大家最钟爱哪一种写法呢?萝卜青菜各有所爱~ 每一个团队都有本身的代码规范和开发模式,但书写 React 组件时 都会以提升代码阅读性、更优的组件性能、易于 bug 追踪为原则。下面咱们就聊聊这三种写法的区别,以及各自所适用场景的最佳实践。github
React.createClass不用多说,咱们最先使用这个方法来构建一个组件“类”,它接受一个对象为参数,对象中必须声明一个render方法,render返回一个组件实例,下面用一个 SwitchButton 组件的例子来看看React.createClass的具体用法:app
1 var React = require('react'); 2 var ReactDOM = require('react-dom'); 3 4 var SwitchButton = React.createClass({ 5 getDefaultProp:function() { 6 return { open: false } 7 }, 8 9 getInitialState: function() { 10 return { open: this.props.open }; 11 }, 12 13 handleClick: function(event) { 14 this.setState({ open: !this.state.open }); 15 }, 16 17 render: function() { 18 var open = this.state.open, 19 className = open ? 'switch-button open' : 'btn-switch'; 20 21 return ( 22 <label className={className} onClick={this.handleClick.bind(this)}> 23 <input type="checkbox" checked={open}/> 24 </label> 25 ); 26 } 27 }); 28 29 ReactDOM.render( 30 <SwitchButton />, 31 document.getElementById('app') 32 );
React 升级到 v0.13 后就支持了 ES6 的class语法,咱们可使用class App extends React.Component{...}的方式建立组件,这也是目前官方推荐建立有状态组件的方式。用 ES6 重写上面 SwitchButton 组件的例子:框架
1 import React from 'react' 2 import { render } from 'react-dom' 3 4 class SwitchButton extends React.Component { 5 constructor(props) { 6 super(props) 7 this.state = { 8 open: this.props.open 9 } 10 this.handleClick = this.handleClick.bind(this) 11 } 12 13 handleClick(event) { 14 this.setState({ open: !this.state.open }) 15 } 16 17 render() { 18 let open = this.state.open, 19 className = open ? 'switch-button open' : 'btn-switch' 20 21 return ( 22 <label className={className} onClick={this.handleClick}> 23 <input type="checkbox" checked={open}/> 24 </label> 25 ) 26 } 27 } 28 29 SwitchButton.defaultProps = { 30 open: false 31 } 32 33 render( 34 <SwitchButton />, 35 document.getElementById('app') 36 )
与React.createClass建立组件的不一样之处:less
import
与这里使用了 ES6 的import语句替代require()方法导入模块,其中import {render}能够直接从模块中导入变量名,这种写法更加简洁直观。dom
初始化 state
React 使用 ES6 的“类”继承实现时,去掉了getInitialState这个 hook 函数,state的初始化放在构造函数方法constructor中声明。函数
this 绑定
React.Component建立组件时,事件函数并不会自动绑定this,须要咱们手动绑定,否则this将不会指向当前组件的实例对象。如下有三种绑定this的方法:组件化
1. 在constructor中使用bind()进行硬绑定post
constructor() { this.handleClick = this.handleClick.bind(this); }
2. 直接在元素上使用bind()绑定
<label className={className} onClick={this.handleClick.bind(this)}>
3. ES6 有个颇有用的语法糖:Arrow Function(箭头函数)它能够很方便的使this直接指向class SwitchButton(它的做用等同于你们熟悉的var self = this,但后者会让代码变得混乱,Arrow Function 就很好的解决了这一问题)
<label className={className} onClick={()=>this.handleClick()}>
React.createClass和React.Component均可以用来建立有状态的组件,而 无状态组件 - Stateless Component 是 React 在 v0.14 以后推出的。
它的出现 是由于随着应用复杂度不断提高和组件本数量的增长,组件按各自职责被分红不一样的类型,因而有一种只负责展现的纯组件出现了,它的特色是不须要管理状态state,数据直接经过props传入,这也符合 React 单向数据流的思想。
对于这种无状态的组件,使用函数式的方式声明,会使得代码的可读性更好,并能大大减小代码量,Arrow Function 则是函数式写法的最佳搭档:
1 const Todo = (props) => ( 2 <li 3 onClick={props.onClick} 4 style={{textDecoration: props.complete ? "line-through" : "none"}} 5 > 6 {props.text} 7 </li> 8 )
上面定义的 Todo 组件,输入输出数据彻底由props决定,并且不会产生任何反作用。对于props为 Object 类型时,咱们还可使用 ES6 的解构赋值:
1 const Todo = ({ onClick, complete, text, ...props }) => ( 2 <li 3 onClick={onClick} 4 style={{textDecoration: complete ? "line-through" : "none"}} 5 {...props} 6 > 7 {props.text} 8 </li> 9 )
无状态组件通常会搭配高阶组件(简称:OHC)一块儿使用,高阶组件用来托管state,Redux 框架就是经过 store 管理数据源和全部状态,其中全部负责展现的组件都使用无状态函数式的写法。
这种模式被鼓励在大型项目中尽量以简单的写法 来分割本来庞大的组件,而将来 React 也会面向这种无状态的组件进行一些专门的优化,好比避免无心义的检查或内存分配。因此建议你们尽量在项目中使用无状态组件。
固然,无状态组件也不是万金油,好比它不支持"ref",缘由很简单,由于 React 调用它以前,组件不会被实例化,天然也就没有"ref",(ref和findDOMNode实际上打破了父子组件之间仅经过 props 来传递状态的约定,违背了 React 的原则,须要避免)。
Facebook 官方早就声明 ES6React.Component将取代React.createClass。随着 React 不断发展,React.createClass暴露出一些问题:
总的来讲:无状态函数式写法 优于React.createClass,而React.createClass优于React.Component。
参考资料: