本文由云 + 社区发表

作者:前端林子

1. 七个可选的生命周期

可以结合下图来看:

img

(1) componentWillMount()仅在 render() 方法前被调用一次,如果在该方法中调用了 setState 方法去改变组件的状态值,那么调用 render()后,将会直接看到改变过了的状态值,并且不论状态值怎么改变,componentWillMount() 都不会再被调用。

(2) componentDidMount()仅在 render() 方法后被立即调用一次(客户端),相对于父组件而言,该方法在子组件中会先被调用。如果需要使用一些 JaveScript 框架或者类似于 setInterval() 这样的方法,建议在该方法内使用。

(3) ShouldComponentUpdate(object nextProps, object nextState) 在初始渲染调用 render()方法时不会被调用,后面在接受到新的 state 或者 props 时,在 render() 方法前被调用。为防止一些潜在的 bug,该方法默认总是返回 true。如果你确定 state 及 props 改变后不需要渲染组件,那么也可以指定返回 false,需要注意的是,这样的结果会导致后面的 render()、componentWillUpdate()、componentDidUpdate() 都不会被调用。

一般的,我们可以通过该函数来优化性能:

一个 React 项目需要更新一个小组件时,很可能需要父组件更新自己的状态。而一个父组件的重新更新会造成它旗下所有的子组件重新执行 render() 方法,形成新的虚拟 DOM,再用 diff 算法对新旧虚拟 DOM 进行结构和属性的比较,决定组件是否需要重新渲染

无疑这样的操作会造成很多的性能浪费,所以我们开发者可以根据项目的业务逻辑,在 shouldComponentUpdate() 中加入条件判断,从而优化性能

例如 React 中的就提供了一个 PureComponent 的类,当我们的组件继承于它时,组件更新时就会默认先比较新旧属性和状态,从而决定组件是否更新。值得注意的是,PureComponent 进行的是浅比较,所以组件状态或属性改变时,都需要返回一个新的对象或数组

(4) componentWillReceiveProps(object nextProps) 在初始渲染调用 render()方法时不会被调用,当接收到一个新的 props 时,该方法被调用。我们都知道,如果改变一个状态的值,则会触发 render() 方法,所以可以在这个方法里调用 setState()方法去改变一个状态的值,当该方法接收到新的 props 时,setState() 就可以避免一次额外的 render()了。 在这个方法里,尤其需要注意一点,就是接收到新的 props 一定会触发 render() 方法,但是 render() 方法被触发不一定是因为接收到了新的 props

(5) componentWillUpdate(object nextProps, object nextState) 在初始渲染调用 render()方法时不会被调用,当接收到新的 props 及 state 时,在 render() 方法之前被调用。

不要在此方法再去更新 props 或者 state

(6) componentDidUpdate(object prevProps, object prevState) 在初始渲染调用 render() 方法时不会被调用,当组件更新被刷新到 DOM 之后被立即调用。

可以在这里访问,并修改 DOM

(7) componentWillUnmount() 在组件从 DOM 上卸载前被调用,在这个方法里面,我们主要是完成一些清除操作,比如说清除掉一些过时了的定时器等。

2. 执行顺序及次数

(1) getDefaultProps(),调用 1 次

(2) getInitialState(),调用 1 次

(3) componentWillMount(),调用 1 次

(4) render(),调用 >=1 次

(5) componentDidMount():仅客户端,调用 1 次

(6) componentWillReceiveProps(object nextProps),调用 >=0 次

(7) ShouldComponentUpdate(object nextProps, object nextState),调用 >=0 次

(8) componentWillUpdate(object nextProps, object nextState),调用 >=0 次

(9) render(),调用 >=1 次

(10) componentDidUpdate(object prevProps, object prevState),调用 >=0 次

(11) componentWillUnmount(),调用 1 次

3. 实例

我写了一个小 demo 可直接在浏览器里运行,大家可以通过控制台查看父组件、子组件中的各生命周期调用的顺序:

<!DOCTYPE html>

<html>

&lt;head&gt;

    &lt;script src="https://fb.me/react-15.2.0.js"&gt;&lt;/script&gt;

    &lt;script src="https://fb.me/react-dom-15.2.0.js"&gt;&lt;/script&gt;

    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"&gt;&lt;/script&gt;

&lt;/head&gt;

<body>

&lt;div id="app-container"&gt;&lt;/div&gt;

&lt;script type="text/babel"&gt;
    var SubCounter = React.createClass({
        componentWillReceiveProps:function() {
            console.log('9、子组件将要接收到新属性');
        },

        shouldComponentUpdate:function(newProps, newState) {
            console.log('10、子组件是否需要更新');
            if (newProps.number &lt; 5) return true;
            return false
        },

        componentWillUpdate:function() {
            console.log('11、子组件将要更新');
        },

        componentDidUpdate:function() {
            console.log('13、子组件更新完成');
        },

        componentWillUnmount:function() {
            console.log('14、子组件将卸载');
        },

        render:function() {
            console.log('12、子组件挂载中');
            return (
                    &lt;p&gt;{this.props.number}&lt;/p&gt;
            )
        }
    });

    var Counter = React.createClass({
       
        getInitialState:function(){
            return(
                this.state={
                    number:0
                }
            )
        },

        componentWillMount:function(){
            console.log('3、父组件挂载之前');
        },

        componentDidMount:function(){
            console.log('5、父组件挂载完成');
        },

        shouldComponentUpdate:function(newProps, newState) {
            console.log('6、父组件是否需要更新');
            if (newState.number&lt;15) return true;
            return false
        },

        componentWillUpdate:function() {
            console.log('7、父组件将要更新');
        },

        componentDidUpdate:function() {
            console.log('8、父组件更新完成');
        },

        handleClick : function(){
            this.setState({
                number: this.state.number + 1
            })
        },
        render:function() {
            console.log('4、render(父组件挂载)');
            return (
                &lt;div&gt;
                    &lt;p&gt;{this.state.number}&lt;/p&gt;
                    &lt;button onClick={this.handleClick}&gt;+&lt;/button&gt;
                    {this.state.number&lt;10?&lt;SubCounter number={this.state.number}/&gt;:null}
                &lt;/div&gt;
            )
        }
    });        

    ReactDOM.render(&lt;Counter /&gt;, document.getElementById('app-container'));

&lt;/script&gt;

</body>

</html>

复制代码

点击一次按钮,通过控制台可以看到:

img

4. 小结

本文主要是图文结合地介绍了 react 的生命周期及执行顺序,同时附上了一个实例,可以清楚地看到父组件、子组件的调用顺序。如存在问题,欢迎指正 ~~~

此文已由腾讯云 + 社区在各渠道发布

获取更多新鲜技术干货,可以关注我们腾讯云技术社区 - 云加社区官方号及知乎机构号

感谢    赞同    分享    收藏    关注    反对    举报    ...