React系列---React(三)組件的生命週期

React系列---React(一)初識React
React系列---React(二)組件的prop和state
React系列---之React(三)組件的生命週期react


React嚴格定義了組件的生命週期,共3個過程:
1) 裝載過程(Mount):組件第一次在DOM樹中渲染的過程;
2) 更新過程(Update):組件被從新渲染的過程;
3) 卸載過程(Unmount):組件從DOM樹中刪除的過程。
clipboard.pnggit

三種不一樣的過程,React庫會依次調用組件的一些生命週期函數。因此,定義一個React組件,實際上就是定製這些生命週期函數。github

組件裝載過程

裝載過程依次調用的生命週期函數:
constructor
getInitialState
getDefaultProps
componentWillMount
render
componentDidMountsegmentfault

constructor

ES6中每一個類的構造函數,創造一個組件實例,固然會調用對應的構造函數。瀏覽器

並非每一個組件都須要定義構造函數。後面會看到無狀態React組件是不須要定義構造函數的。

React組件須要構造函數,是爲了如下目的:
1) 初始化state,由於生命週期中任何函數都有可能訪問state,構造函數是初始化state的理想場所;
2) 綁定成員函數的this環境。函數

getInitialState和getDefaultProps

這2個函數,只有在經過React.createClass方法創造的組件類纔會發生做用。這是過期的方法,ES6創造的組件中用不到。性能

假如用React.createClass定義組件Sample,設定內部狀態foo初始值爲bar,同時設定sampleProp的prop初始值爲0,代碼以下:this

const Sample = React.createClass({
    getInitialState: function(){
        return {foo: 'bar'};
    },
    getDefaultProps: function() {
        return {sampleProp: 0}
    }
});

用ES6的話,在構造函數中給this.state賦值完成狀態初始化,給類的屬性defaultProps賦值指定props初始值:spa

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來操做渲染過程。code

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和其餘操縱DOM的庫(如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(三)組件的生命週期

相關文章
相關標籤/搜索