若是你熟悉avalon,使用過 data-include-rendered 和 data-include-loaded 等回調方法,那麼你會很好地理解React組件的各個生命週期。html
說白了其實就是React組件狀態變化先後的時間點,咱們能夠利用生命週期的接口在相應的時間點作回調操做。react
React的官方文檔說起了以下幾個組件的生命週期:git
Mounting/組件掛載相關: github
Updating/組件更新相關:app
Unmounting/組件移除相關:框架
下面咱們將經過一些實例來理解它們。順便說下本文的示例均可以在個人github上下載到。dom
一. Mounting/組件掛載相關ide
componentWillMount學習
在組件掛載以前執行操做,但僅執行一次,即便屢次重複渲染該組件,或者改變了組件的state:this
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillMount</title> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var i = 0; var Component1 = React.createClass({ componentWillMount: function(){ console.log(i++) }, getInitialState: function() { return { isClick: !1 } }, clickCb: function() { this.setState({ isClick : !0 }) }, render: function() { return <div onClick={this.clickCb}>isClick:{this.state.isClick? 'yes' : 'nope'}</div> } }); var div = document.getElementById('a'); React.render( <Component1 />,div ); React.render( <Component1 />,div ); </script> </body> </html>
若是但願該回調能執行屢次,可使用 React.unmountComponentAtNode(該方法咱們下篇文章再介紹)移除掉已有的組件,而後再從新 render:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillMount</title> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var i = 0; var Component1 = React.createClass({ componentWillMount: function(){ console.log(i++) }, getInitialState: function() { return { isClick: !1 } }, clickCb: function() { this.setState({ isClick : !0 }) }, render: function() { return <div onClick={this.clickCb}>isClick:{this.state.isClick? 'yes' : 'nope'}</div> } }); var div = document.getElementById('a'); React.render( <Component1 />,div ); React.unmountComponentAtNode(div); //移除掉已有組件 React.render( <Component1 />,div ); </script> </body> </html>
能夠看到輸出了兩行:
componentDidMount
顧名思義能夠猜到這個是在組件初始化掛載以後執行的,好比咱們能夠利用它來隱藏掉頁面的loading菊花層。
同 componentWillMount 同樣,同一個組件重複渲染只執行一次,卸載組件後從新渲染能夠從新觸發一次:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentDidMount</title> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <div id="b">123</div> <script type="text/jsx"> var i = 0, div = document.getElementById('a'), div2 = document.getElementById('b'); var Component1 = React.createClass({ componentDidMount: function(){ console.log(i++) }, clickCb: function() { React.render( <Component1 />, div2 ); }, render: function() { return <div onClick={this.clickCb}>點我給下一個div掛載組件</div> } }); React.render( <Component1 />, div ); //React.unmountComponentAtNode(div); //移除掉已有組件 React.render( <Component1 />, div ); </script> </body> </html>
注意上述代碼點擊div1時會將組件掛載到div2上,觸發div2的組件的 componentDidMount 回調(畢竟div1和div2上的組件並不是同一個)。
二. Updating/組件更新相關:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillReceiveProps</title> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <div id="b">123</div> <script type="text/jsx"> var i = 0, div = document.getElementById('a'), div2 = document.getElementById('b'); var Component1 = React.createClass({ componentWillReceiveProps: function(){ console.log(i++) }, clickCb: function() { React.render( <Component1 />, div2 ); }, render: function() { return <div onClick={this.clickCb}>點我給下一個div掛載組件</div> } }); React.render( <Component1 />, div //初始化不會觸發componentWillReceiveProps ); React.render( <Component1 />, div //重複渲染會觸發componentWillReceiveProps ); React.unmountComponentAtNode(div); //移除掉已有組件 React.render( <Component1 />, div //初始化不會觸發componentWillReceiveProps ); </script> </body> </html>
注意咱們移除掉組件再掛載的時候,至關於從新初始化渲染了組件(獲得的props是初始化props而不是新props),故不會觸發 componentWillReceiveProps 。
而當咱們在div2掛載了組件後再點擊div2來從新渲染它的組件,會觸發 componentWillReceiveProps :
該接口帶有一個參數 nextProps,咱們能夠利用它來獲取新 props 的值(this.props 獲取到的是當前的,也就是舊的 props):
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillReceiveProps</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var i = 0, div = document.getElementById('a'), render = function(){ React.render( <Component1 i={i++} />, div ); }; var Component1 = React.createClass({ componentWillReceiveProps: function(nextProps){ console.log(this.props.i, nextProps.i) }, render: function() { return <div onClick={render}>props.i的值是:{this.props.i}</div> } }); render(); </script> </body> </html>
經過點擊div1的組件,能夠輸出 componentWillReceiveProps 時間點(這時候還沒從新執行渲染)的 props 以及即將獲取到的新 props,執行以下:
shouldComponentUpdate
前面我們學習的接口都是叫 componentXXX,而這個把 should 放在前面,翻譯過來其實就是「是否應該XXX」的意思,那麼能夠把該接口直接理解爲「組件是否應該作更新」的意思,即其了一個決定組件要不要從新渲染的做用。
該接口實際是在組件接收到了新的 props 或者新的 state 的時候(該時間點render還沒執行哦)會當即調用,而後經過返回值(Boolean)來決定是否要從新渲染當前的組件。
該接口帶有兩個參數,第一個參數表示新的props,第二個參數表示新的state。
咱們來個例子,比方要求div要點擊3次以後,才從新渲染自身組件:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>shouldComponentUpdate</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var div = document.getElementById('a'); var Component1 = React.createClass({ getInitialState: function(){ return { i : 0 } }, shouldComponentUpdate: function(nextProps, nextState){ console.log( this.state.i, nextState.i ); return nextState.i > 3 ? true : false; //返回true纔會渲染組件 }, clickCb: function(){ this.setState({ i : this.state.i + 1 }) }, render: function() { return <div onClick={this.clickCb}>state.i的值是:{this.state.i}</div> } }); React.render( <Component1 />, div ); </script> </body> </html>
執行以下,點擊第四次以後纔會渲染組件,在div裏顯示出正確的新state.i:
componentWillUpdate
同 shouldComponentUpdate 同樣,在組件收到新的 props 或者 state 的時候會當即調用,並且也有着倆個參數來獲取新的 props 和 state。
不過本接口會在 shouldComponentUpdate 執行並返回了 true 的時候纔會被調用。咱們拿上一個代碼示例作點小修改:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillUpdate</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var div = document.getElementById('a'); var Component1 = React.createClass({ getInitialState: function(){ return { i : 0 } }, shouldComponentUpdate: function(nextProps, nextState){ console.log( this.state.i, nextState.i ); return nextState.i > 3 ? true : false; //返回true纔會執行componentWillUpdate並從新渲染組件 }, componentWillUpdate: function(nextProps, nextState){ console.log( 'yoyoyo', this.state.i, nextState.i ); }, clickCb: function(){ this.setState({ i : this.state.i + 1 }) }, render: function() { return <div onClick={this.clickCb}>state.i的值是:{this.state.i}</div> } }); React.render( <Component1 />, div ); </script> </body> </html>
利用這個接口,咱們能夠在組件要從新渲染以前作一些須要的改動。
componentDidUpdate
Did表示完成時狀態,故該接口會在組件更新、從新渲染完畢了以後才觸發,它和 componentWillUpdate 同樣有着倆個參數來獲取新的 props 和 state。
咱們繼續拿上一個例子來作修改:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentDidUpdate</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <script type="text/jsx"> var div = document.getElementById('a'); var Component1 = React.createClass({ getInitialState: function(){ return { i : 0 } }, shouldComponentUpdate: function(nextProps, nextState){ console.log( this.state.i, nextState.i ); return nextState.i > 3 ? true : false; //返回true纔會執行componentWillUpdate並從新渲染組件 }, componentDidUpdate: function(nextProps, nextState){ console.log( '已經渲染完畢咯', this.state.i, nextState.i ); }, componentWillUpdate: function(nextProps, nextState){ console.log( '還沒渲染哦', this.state.i, nextState.i ); }, clickCb: function(){ this.setState({ i : this.state.i + 1 }) }, render: function() { return <div onClick={this.clickCb}>state.i的值是:{this.state.i}</div> } }); React.render( <Component1 />, div ); </script> </body> </html>
執行以下:
三. Unmounting/組件移除相關:
componentWillUnmount
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>componentWillUnmount</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div id="a">123</div> <div id="b"><p>這裏是div2,點擊我會移除上面div的組件</p></div> <script type="text/jsx"> var div = document.getElementById('a'), div2 = document.getElementById('b'); var Component1 = React.createClass({ DOMArr : [], getInitialState: function(){ return { i : 0 } }, componentDidUpdate: function(nextProps, nextState){ var dom = document.createElement('p'); dom.innerText = this.state.i; div2.appendChild(dom); this.DOMArr.push(dom); }, componentWillUnmount: function(){ if(!this.DOMArr.length) return; var i = 0; while(i < this.DOMArr.length){console.log(i); div2.removeChild(this.DOMArr[i++]); //移除componentDidUpdate裏添加過的DOM } }, clickCb: function(){ this.setState({ i : this.state.i + 1 }) }, render: function() { return <div onClick={this.clickCb}>state.i的值是:{this.state.i}</div> } }); React.render( <Component1 />, div ); div2.addEventListener('click',function(){ React.unmountComponentAtNode(div) //點擊div2則卸載掉第一個div裏的組件 }, false) </script> </body> </html>
執行以下:
四. getDefaultProps 和 getInitialState
在《React 引領將來的用戶界面開發框架》一書中,還把 getDefaultProps 和 getInitialState 列入了組件生命週期的「實例化」階段。
getDefaultProps
該方法是全部咱們說起的方法中最早觸發的,你能夠在該方法裏 return 一個對象來做爲組件默認的Props值(固然若是父組件傳進來了props,則以傳進來的爲主),它只在組件初次掛載到頁面上時觸發一次,即便你從新掛載了組件。
getInitialState
這個在第一篇文章的時候就介紹過了,它用於給組件初始化state的值,調用該方法要求必須 return 一個對象或者null,不然會報錯。該方法在組件每次實例化(也就是掛載)的時候都會觸發。
咱們來段簡單的代碼輔助理解:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>getDefaultProps 和 getInitialState</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <div></div> <div></div> <script type="text/jsx"> var diva = document.getElementsByTagName('div')[0], divb = document.getElementsByTagName('div')[1]; var Component1 = React.createClass({ getDefaultProps: function(){ console.log('getDefaultProps'); return { name : Date.now() } }, getInitialState: function(){ console.log('getInitialState'); return null; //必須返回一個null或對象,不然會報錯 }, render: function() { console.log(Date.now()); return <div name={this.props.name}>我只是一個安靜的div</div> } }); React.render( {/* 觸發一次 getDefaultProps 和 getInitialState */} <Component1 />, diva ); React.render( {/* getDefaultProps 和 getInitialState都不觸發 */} <Component1 />, diva ); React.unmountComponentAtNode(diva); React.render( {/* 觸發一次getInitialState */} <Component1 name="a"/>, diva ); React.render( {/* 觸發一次getInitialState */} <Component1/>, divb ); </script> </body> </html>
執行結果:
至此咱們便學習完了React組件裏的共九個聲明週期的接口,最後出道題給你們思考下,下面的代碼輸出的順序應該是什麼呢:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>題目</title> <style> div:active{color:orangered;} </style> <script src="react.js"></script> <script src="JSXTransformer.js"></script> </head> <body> <script type="text/jsx"> var Component1 = React.createClass({ getDefaultProps: function(){ console.log('getDefaultProps') }, getInitialState: function(){ console.log('getInitialState'); return null }, componentWillMount: function(){ console.log('componentWillMount') }, componentDidMount: function(){ console.log('componentDidMount') }, componentWillReceiveProps: function(){ console.log('componentWillReceiveProps') }, shouldComponentUpdate: function(){ console.log('shouldComponentUpdate'); return true; }, componentWillUpdate: function(){ console.log('componentWillUpdate') }, componentDidUpdate: function(){ console.log('componentDidUpdate') }, componentWillUnmount: function(){ console.log('componentWillUnmount') }, render: function() { return <div>我只是一個安靜的div</div> } }); React.render( <Component1 />, document.body ); React.render( <Component1 />, document.body ); React.unmountComponentAtNode(document.body) </script> </body> </html>
建議思考完了再往下滾動看答案吧,若是想不起來,能夠翻到文章前面在回顧一下,多溫習多思考,老是好習慣。
順便再提一下,本文的所有示例均可以在個人github上下載到。
關於上方問題的答案以下:
最後建議你們多實踐,不侷限於看文章。也但願本文能對你有所幫助,共勉~