简介 在目前看来react毫无疑问是最流行的框架,没有之一。 之前看了阮一峰写的react基础,写的非常好。但这个教程在现在看来,有些用法已经过时了。 所以我打算用ES6语法写一篇这样的文章。 说到ES6,大家都知道目前还不能兼容所有浏览器,因此我们要用到babel。把ES6代码转化为浏览器识别的ES5代码。 在这里我用webpack搭建的环境,没用过webpack的,可以看我之前写的文章。这里就不一一讲解。
一、HTML模板
这里的root是根节点,下文就会用到 二.render
render是react的最基本语法,用于将HTML转化为语言,并插入指定的节点。 用react时我们要引进两个库 react和react-dom,用ES6中的import引进,后面每次都需要引入,不然无法执行代码。
import React,{ component } from 'react'; import { render } from 'react-dom'; render( <h1>hello world!</h1>, document.getElementById('root') )运行结果如下:
hello world!
三、JSX语法
上一节的代码,HTML语言直接写在javascript语言中,不加任何引号,这就是JSX语法。 它允许HTML和JSX混写。
render( <div> { names.map(function(item,index){ return <h1 key={index}>hello {item}</h1> }) } </div>, document.getElementById('root') )上面的代码体现了JSX的基本语法规则:遇到HTML(以<开头),就用HTML解析规则;遇到代码块(以{开头),就用javascript规则解析。 上面的代码运行结果如下: hello 张三 hello 李四 hello 王五
JSX允许在模板中插入javascript变量,如果这个这个变量是一个数组,会展开数组所有的成员。
var arr = [ <h1>hello 张三</h1>, <h1>hello 李四</h1> ]; render( <div>{arr}</div>, document.getElementById('root') )执行结果如下:
hello 张三 hello 李四 四、组件
React允许将代码封装成组件(component),然后像插入普通HTML一样,在网页中插入组件。用class定义一个类组件。
class Hello extends Component{ render(){ return ( <h1>hello {this.props.name}</h1> ) } } render( <Hello name="张三" />, document.getElementById('root') )上面代码,Hello 就是用ES6的class定义的一个类。模板插入时,就会自动生成Hello的一个实例(下文的“组件”都指组件类的实例)。所有的组件类都有自己的render方法,用于输出组件。 PS:
1.组件类的第一个字母必须大写,否则无法显示内容。比如,Hello不能写成hello。 2.组件类只能包含一个顶层标签,否则会报错。 class Hello extends Component{ render(){ return ( <h1>hello { this.props.name }</h1> <h1>小</h1> ) } } render( <Hello name="张三" />, document.getElementById('root') )上面这个会报错。 但这样不会报错,原因正在探索中
class Hello extends Component{ render(){ return ( <h1>hello { this.props.name}</h1>, <h1>小</h1> ) } } render( <Hello name="张三" />, document.getElementById('root') )运行的结果为 小 父组件<Hello name="张三" />中的name属性传递给子组件。this.props.name用于获取父组件传递的值。 组件的用法和原生的HTML标签完全一样,可以加入任意属性。比如,<Hello name="张三" /> 就是Hello组件加入的一个name属性,值为张三。组件的属性可以在组件的this.props对象上获取。比如那么属性就可以通过this.props.name属性读取。 添加组件属性,需要注意的地方是class属性要写成className,for属性要写成htmlFor。这是因为class和for是javascript的保留字。 五、this.props.children this.props对象的属性与组件的属性一一对应,但是有一个例外就是this.props.children属性,它表示组件的所有子节点。
class NoteList extends Component{ render(){ console.log(this.props.children); return( <div> { React.Children.map(this.props.children,function(item,index){ return <h1 key={index}>{item}</h1> }) // this.props.children.map(function(item,index){ // return <h1 key={index}>{item}</h1> // }) } </div> ) } } render( <NoteList> <span>hello</span> <span>world</span> </NoteList>, document.getElementById('root') )上面代码的NoteList 组件有两个span子节点,它们都可以通过this.props.children读取,运行结果如下: hello world 这里要注意的是,this.props.chidren的值有3种可能 第一种可能是:没有子节点,这时它的值为undefined 第二种可能是:只有一个子节点,这时它的值是一个对象。 第三种可能是: 有两个及以上子节点时,它的值是一个数组。 如果用下面这种方法遍历时需要注意:
this.props.children.map(function(item,index){ return <h1 key={index}>{item}</h1> }) 遍历的是数组还是对象。于是react提供了一种方法,React.Children.map,不管你是数组还是对象我都可以遍历。六、propTypes
组件的属性接受任意值,字符串、对象、函数等等都可以。有时我们需要一种机制,验证别人使用组件时,提供的参数是否符合要求。 组件类的Proptypes属性,就是用来验证组件实例的属性是否符合要求。
class Hello extends Component{ constructor(props){ super(props); } render(){ return <h1>{this.props.title}</h1> } } Hello.propTypes = { title: React.PropTypes.string }上面的Hello组件有一个title属性。PropTypes告诉React,这个title属性时必须的,而且它的值必须是字符串,我们给它设置一个数字,看会有什么结果?
var data = 123; render( <Hello title={data} />, document.getElementById('root') )结果呢?报错了
Warning: Failed prop type: Invalid prop `title` of type `number` supplied to `Hello`, expected `string`. in Hello如果写成这样,title属性就不会通过验证了,控制台会会报错。
var data = '123';此外,defaultProps可以设置组件属性的默认值。
class Hello extends Component{ constructor(props){ super(props); } render(){ return <h1>{this.props.title}</h1> } } Hello.defaultProps = { title: 'hello world' } render( <Hello />, document.getElementById('root') )上面的代码会输出
hello world
七、获取真实的DOM节点
八、this.state
组件免不了要和用户互动,React的一大创新,就是将组件看成一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化。从而触发重新触发UI。
class Hello extends Component{ constructor(props){ super(props); this.state = { liked: false } } handleClick(){ this.setState({liked:!this.state.liked}) } render(){ var text = this.state.liked ? '喜欢':'不喜欢'; return( <p onClick={this.handleClick.bind(this)}> You {text} toggle </p> ) } } render( <Hello />, document.getElementById('root') )我们逐个代码块分析
constructor(props){ super(props); this.state = { liked: false } }this.state 初始化状态,所以第一次渲染完之后就是不喜欢
handleClick(){ this.setState({liked:!this.state.liked}) }当我们发生点击事件时,就会取它相反的状态。每次修改状态之后就会调用render方法,就会再次渲染页面。 由于this.props和this.state都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是:this.props表示哪些一旦定义就不在改变的特性,而this.state是随着用户互动而产生变化的特性。
九、表单
用户在表单填入的内容属于用户和组件的互动,所以不能用this.props.
class Hello extends Component{ constructor(props){ super(props); this.state = { value: 'Hello!' } } handleClick(){ this.setState({liked:!this.state.liked}) } handleChange(e){ this.setState({value: e.target.value}) } render(){ var value = this.state.value; return( <div> <input type="text" value={value} onChange={this.handleChange.bind(this)} /> <p>{value}</p> </div> ) } } render( <Hello />, document.getElementById('root') )上面的代码中,文本框中的值不能通过this.props.value读取,要定义onChange的回调函数。通e.target.value读取用户输入的值。textarea元素、select元素、radio元素都属于这种情况。 十、组件的生命周期
组件的生命周期分为3个状态:
Mounting: 已插入真实的DOM Updating:正在被重新渲染 UnMounting: 已移除真实的DOMReact为每个状态都提供了两种状态函数,will函数在进入状态之前调用,did函数在进入状态之后调用。三种状态共计5种处理函数。
ComponentWillMount() ComponentDidMount() componentWillUpdate(object nextProps,object nextState) componentDidUpdate(object prevProps,object prevState) componentWillUnMount()此外,React还提供两种特殊状态的处理函数。
componentWillReceivesProps(object nextProps)已加载组件收到新的参数时调用。 shouldComponentUpdate(object nextProps,object nextState)组件判断是否重新渲染时调用 class Hello extends Component{ constructor(props){ super(props); this.state = { opacity: 1.0 } } componentDidMount(){ var opacity = this.state.opacity; this.timer = setInterval(function(){ opacity -= 0.05; if(opacity < 0.1){ opacity = 1; } this.setState({opacity: opacity}) }.bind(this),100) } render(){ return ( <h1 style={{opacity:this.state.opacity}}>Hello {this.props.name}</h1> ) } } render( <Hello name="xiao" />, document.getElementById('root') )十一、 Ajax 组件的数据来源是通过ajax请求从服务器获取,可以使用componentDidMount()方法设置ajax请求,等请求成功之后,再用this.setState方法重新渲染UI。
class Hello extends Component{ constructor(props){ super(props); this.state = { username: '', lastGistUrl: '' } } componentDidMount() { $.get(this.props.source, function(result) { var lastGist = result[0]; //if (this.isMounted()) { this.setState({ username: lastGist.owner.login, lastGistUrl: lastGist.html_url }); //} }.bind(this)); } render(){ return( <div> {this.state.username} <a href={this.state.lastGistUrl}>here</a> </div> ) } } render( <Hello source="https://api.github.com/users/octocat/gists" />, document.getElementById('root') )
