解讀React源碼(三):生命週期的管理藝術

前言

React的主要思想是經過構建可複用組件來構建頁面.
所謂組件,其實就是有限狀態機(FSM),經過狀態渲染對應的界面,且每一個組件都有本身的生命週期,
它規定了組件的狀態和方法須要在哪一個階段改變和執行.react

class Collections extends Component {
    constructor(props) {
        super(props);
        this.state = {
            a: 1
        }
    }
    add = () => {
        this.setState({
            a: this.state.a + 1
        });
    }
    componentWillMount() {
        console.log('componentWillMount');
    }
    componentDidMount() {
        console.log('componentDidMount');
        this.setState({
            a: 2
        });
    }
    componentWillUnMount() {
        console.log('componentWillUnMount');
    }
    render() {
        console.log('render');
        return (
            <div className="collections">
                { this.state.a }
                <Example a={ this.state.a } />
                <button onClick={ this.add }>btn</button>
            </div>
        );
    }
}
import React, { Component, PropTypes } from 'react';

export default class Example extends Component {
    static propTypes = {
        a: PropTypes.number.isRequired
    };
    static defaultProps = {
        a: 0
    }; 
    constructor(props) {
        super(props);
        this.state = {
            b: 1
        };
    }
    add = () => {
        this.setState({
            b: this.state.b + 1
        });
    }
    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
        if (nextProps.a === 4) {
            this.setState({
                b: 1000
            });
        }
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log(nextProps);
        console.log(nextState);
        if (nextState.b === 4) {
            return false;
        }
        return true;
    }
    componentWillMount() {
        console.log('子組件componentWillMount');
    }
    componentDidMount() {
        console.log('子組件componentDidMount');
    }
    render() {
        console.log('子組件render');
        return (
            <div>
                <p>a:{ this.props.a }</p>
                <p>b:{ this.state.b }</p>
                <button onClick={ this.add }>子組件add</button>
            </div>
        );
    }
}

初探react生命週期

當首次掛載組件時,按順序執行getDefaultProps,getInitialState,componentWillMount,render,componentDidMount
當卸載組件時,執行componentWillUnMount
當再次渲染組件時,組件接收到更新狀態,此時按順序執行componentWillReceiveProps,shouldComponentUpdate,
componentWillUpdate,render和componentDidUpdate異步

使用createClass建立自定義組件

createClass是建立自定義組件的入口方法,負責管理生命週期方法中的getDefaultProps.
該方法在整個生命週期中只執行一次,這樣全部實例初始化的props將會被共享.
經過createClass建立自定義組件,利用原型繼承ReactClassComponent父類,
按順序合併minin,設置初始化defaultProps,返回構造函數.
當使用ES6 classes編寫React組件時,class MyComponent extends React.Component
其實就是調用內部方法createClass建立組件.函數

var ReactClass = {
    // 建立自定義組件
    createClass: function(spec) {
        var Constructor = function(props, context, updater) {
            // /自動綁定
            if (this._reactAutoBindPairs.length) {
                bindAutoBindMethods(this);
            }

            this.props = props;
            this.context = context;
            this.refs = emptyObject;
            this.updater = updater || ReactNoopUpdateQueue;
            this.state = null;
            // ReactClass沒有構造函數,經過getInitialState和componentWillMount來代替
            var initialState = this.getInitialState ? this.getInitialState() : null;
            this.state = initialState;
        };

        // 原型繼承父類
        Constructor.prototype = new ReactClassComponent();
        Constructor.prototype.constructor = constructor;
        Constructor.prototype._reactAutoBindPairs = [];

        // 合併mixin
        injectedMixins.forEach(
            minSpecIntoComponent.bind(null, Constructor)
        );

        minSpecIntoComponent(Constructor, spec);

        // 全部mixin合併後初始化defaultProps(在整個生命週期中,getDefaultProps只執行一次)
        if (Constructor.getDefaultProps) {
            Constructor.defaultProps = Constructor.getDefaultProps();
        }
        // 減小查找並設置原型的時間
        for (var methodName in ReactClassInterface) {
            if (!Constructor.prototype[methodName]) {
                Constructor.prototype[methodName] = null;
            }
        }

        return Constructor;
    }
};

MOUNTING

mountComponent負責管理生命週期中的getInitialState,componentWillMount,render和componentDidMount
因爲getDefaultProps是經過構造函數進行管理的,因此也是整個生命週期中最早開始執行的.
react是利用更新隊列this._pendingStateQueue以及更新狀態this._pendingReplaceState
和this._pendingForceUpdate來實現setState的異步更新機制.
其實,mountComponent本質上是經過遞歸渲染內容的,因爲遞歸的特性,父組件的componentWillMount
在其子組件的componentWillMount以前調用,而父組件的componentDidMount在其子組件的componentDidMount以後調用.oop

RECEIVE_PROPS

updateComponent負責管理生命週期中的componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,
render和componentDidUpdate.
若存在componentWillReceiveProps,則執行.
若是此時在componentWillReceiveProps中調用setState,是不會觸發re-render的,而是會進行state合併.
且在componentWillReceiveProps,shouldComponentUpdate和componentWillUpdate中也仍是沒法獲取到更新後的this.state,
即此時訪問的this.state仍然是未更新的數據,所以只有render和componentDidUpdate中才能獲取到更新後的this.stateui

componentWillReceiveProps(nextProps) {
    console.log(nextProps);
    if (nextProps.a === 4) {
        this.setState({
            b: 1000
        }, () => {
            console.log(this.state.b);
        });
    }
}

UNMOUTING

unmountComponnet負責管理生命週期中的componentWillUnMount
若是存在componentWillUnMount,則執行並重置全部相關參數,更新隊列以及更新狀態,
若是此時在componentWillUpdate中調用setState,是不會觸發re-render的,這是由於
全部更新隊列和更新狀態都被重置爲null,並清除了公共類,完成了組件卸載操做.this

無狀態組件

無狀態組件只是一個render方法,並無組件類的實例化過程,也沒有實例返回.
無狀態組件沒有狀態,沒有生命週期,只是簡單的接受props渲染生成DOM結構,
是一個純粹爲渲染而生的組件.
因爲無狀態組件有簡單,便捷,高效等諸多優勢,因此若是可能的話請儘可能使用無狀態組件.prototype

import React from 'react';

const HelloWorld = (props) => {
    return (
        <div>{ props.a }</div>
    );
}

export default HelloWorld;
相關文章
相關標籤/搜索