React技术栈之React(一)初识React
React技术栈之React(二)组件的prop和state
React技术栈之React(三)组件的生命周期
React严格定义了组件的生命周期,共3个过程:
1) 装载过程(Mount):组件第一次在DOM树中渲染的过程;
2) 更新过程(Update):组件被重新渲染的过程;
3) 卸载过程(Unmount):组件从DOM树中删除的过程。
三种不同的过程,React库会依次调用组件的一些生命周期函数。所以,定义一个React组件,实际上就是定制这些生命周期函数。
组件装载过程
装载过程依次调用的生命周期函数:
constructor getInitialState getDefaultProps
componentWillMount
render
componentDidMount
constructor
ES6中每个类的构造函数,创造一个组件实例,当然会调用对应的构造函数。
React组件需要构造函数,是为了以下目的:
1) 初始化state,因为生命周期中任何函数都有可能访问state,构造函数是初始化state的理想场所;
2) 绑定成员函数的this环境。
getInitialState和getDefaultProps
这2个函数,只有在通过React.createClass方法创造的组件类才会发生作用。这是过时的方法,ES6创造的组件中用不到。
假如用React.createClass定义组件Sample,设定内部状态foo初始值为bar,同时设定sampleProp的prop初始值为0,代码如下:
const Sample = React.createClass({ getInitialState: function(){ return {foo: 'bar'}; },getDefaultProps: function() { return {sampleProp: 0} } });
用ES6的话,在构造函数中给this.state赋值完成状态初始化,给类的属性defaultProps赋值指定props初始值:
class Sample extends React.Component { constructor(props) { super(props); this.state = {foo: 'bar'}; } } Sample.defaultProps = { return {sampleProp: 0}; };
render
React组件可以忽略其他所有函数都不实现,但是一定要实现render函数,因为所有React组件的父类React.Component类对除render之外的生命周期函数都有默认实现。render函数并不做实际的渲染动作,它只负责返回一个JSX描述的结构,最终由React来操作渲染过程。
render函数应该是一个纯函数,完全根据this.state和this.props来决定返回的结果,而且不要产生任何副作用。在render函数中去调用this.setState是错误的,因为一个纯函数不应该引起状态的变化。
componentWillMount和componentDidMount
装载过程中,componentWillMount和componentDidMount分别在render之前和之后调用。
不过,通常不定义componentWillMount函数,因为顾名思义,它发生在将要装载的时候,这个时候一切都迟了,即使再调用this.setState()修改状态也不会引发重新绘制了。换句话说,所有可以在componentWillMount中做的事情,都可以提前到constructor中去做。可以认为这个函数存在的目的就是为了和componentDidMount对称。
而componentDidMount作用就大了。不过要注意的是,componentDidMount被调用时,前置render函数返回的东西必定已经完成了渲染,组件已经被“装载”到DOM树上了。
还是以ControlPanel为例,在ControlPanel中有三个Counter组件,我们修改Counter代码,让装载过程的所有生命周期函数都用console.log输出函数名和caption值,比如,componentWillMount函数如下:
componentWillMount() { console.log('enter componentWillMount ' + this.props.caption); }
在浏览器的console里我们能看到:
enter constructor: First enter componentWillMount First enter render First enter constructor: Second enter compomentWillMount: Second enter render Second enter constructor: Third enter componentWillMount Third enter render Third enter componentDidMount First enter componentDidMount Second enter componentDidMount Third
可以清楚的看到,由于渲染需要一定的时间,所以三个组件的componentDidMount是在最后才连在一起被调用的。
componentWillMount和componentDidMount还有一个区别,就是componentWillMount可以在服务端被调用,也可以在浏览器端被调用;而componentDidMount只能在浏览器端被调用。
componentDidMount中,可通过AJAX获取数据来填充组件内容。在componentDidMount被调用时,组件已经被装载到DOM树了,也可以放心的让React和其他库(如jQuery)配合工作了。
组件更新过程
当组件被装载到DOM树上之后,用户在网页上看到了第一印象,但是要提供更好的交互体验,就要让组件可以随着用户操作改变展现的内容,当props或state被修改时,就会引发组件的更新过程。
更新过程依次调用以下生命周期函数:
1) componentWillReceiveProps
2) shouldComponentUpdate
3) componentWillUpdate
4) render
5) componentDidUpdate
componentWillReceiveProps
当组件的props发生改变时会被调用。父组件的render被调用时,被渲染的子组件也会经历更新过程,不管父组件传给子组件的props有没有改变,都会触发子组件的componentWillReceiveProps。
我们在Counter组件类里增加这个函数定义,并在console输出一些文字:
componentWillReceiveProps(nextProps) { console.log('enter componentWillReceiveProps ' + this.props.caption) }
在ControlPanel组件的render函数中,也做如下修改:
render() { console.log('enter ControlPanel render'); return ( <div style={style}> ... <button onClick={ () => this.forceUpdate() }> Click me to parent! </button> </div> ); }
除了在ControlPanel的render函数入口增加console输出,还增加了一个按钮,当这个按钮被点击时,调用this.forceUpdate(),每个React组件都可以通过forceUpdate()强行引发一次组件重绘。
在网页上点击父组件新增的重绘按钮,看到浏览器console输出:
enter ControlPanel render enter componentWillReceiveProps First enter render First enter componentWillReceiveProps Second enter render Second enter componentWillReceiveProps Third enter render Third
可以看到,父组件引发重绘后,首先是父组件ControllPanel的render被调用,随后依次3个Counter子组件的componentWillReceiveProps和render函数被调用。
然尔,父组件传给三个子组件的props值一直没有变化,这就验证了componentWillReceiveProps并不只是当props值变化时才被调用,父组件render时,子组件的componentWillReceiveProps也会被调用。
在网页中,我们再尝试点击第一个Counter子组件的“+”按钮,可以看到浏览器console输出:
enter render First
明显,只有第一个子组件Counter的render函数被调用,因为组件的this.setState()函数不会引发componentWillReceiveProps调用。
shouldComponentUpdate(nextProps,nextState)
shouldComponentUpdate函数返回一个布尔值,告诉React库这个组件在这次更新过程中是否要继续。返回false会停止更新过程,就此结束,也不会引发后续的渲染了。
这个函数能够大大提升React组件的性能,不管React的组件渲染有多快,如果发现没有必要重新渲染,就干脆不要渲染。
修改Counter组件类增加shouldComponentUpdate函数的定义:
shouldComponentUpdate(nextProps,nextState) { return (nextProps.caption !== this.props.caption) || (nextState.count !== this.state.count); }
componentWillUpdate和componentDidUpdate
如果组件的shouldComponentUpdate返回true,接下来会依次调用componentWillUpdate、render和componentDidUpdate函数。
在介绍componentDidMount函数时,说到可以利用componentDidMount函数执行其他UI代码库,比如jQuery代码。那么现在,组件被更新时,也需要在componentDidUpdate函数再次调用jQuery代码。
组件卸载过程
React组件的卸载过程只涉及一个函数componentWillUnmount,这个函数适合做一些清理工作。这些清理工作往往和componentDidMount有关,比如你在componentDidMount中用非React的方法创造了一些DOM元素,如果撒手不管会造成内存泄漏,那就需要在componentWillUnmount中把这些DOM元素清理掉。
代码:https://github.com/zhutx/reac...
React技术栈之React(一)初识React
React技术栈之React(二)组件的prop和stateReact技术栈之React(三)组件的生命周期