下圖描述了React Native中組件的生命周期:
從上圖中可以看到,React Native組件的生命周期可以分為初始化階段、存在階段和銷毀階段。
實例化階段
實例化階段是React Native組件生命周期的三個階段中最常用的階段,該階段是組件的構建、展示階段,該階段中的幾個方法的功能解析如下:
getDefaultProps:
該函數用於初始化一些默認的屬性。
在組件中可以利用 this.props.* 的方式獲取在這個函數中定義的屬性。
注意:this.props是只讀的區域,組件中不可以修改props中的屬性。
getInitialState:
該函數用於對組件的一些狀態進行初始化。
可以將控制組件狀態的一些變量在這里初始化(通過this.state來獲取值,通過this.setState來修改值)。
注意:一旦調用了this.setState方法,組件就一定會調用render函數對組件進行再次渲染,不過React框架會自動根據DOM的狀態來判斷是否需要真正的渲染。
render:
render函數返回JSX或其他組件來構成DOM(注意:只能返回一個頂級元素)。
在render函數中,只可以通過this.props和this.state來訪問在之前的函數中初始化的數據。
componentDidMount:
在調用了render函數,組件加載成功並被成功渲染出來以后,所要執行的后續操作(如網絡請求等加載數據的操作),一般會在這個函數中進行。因為UI已經被渲染出來了,所以放在這個函數中進行的請求操作,不會出現UI上的錯誤。
注意:如果想要在主類中書寫多個生命周期函數(getInitialState等),需要使用ES 5的語法,如果使用ES 6的語法會報錯。
存在階段和銷毀階段
當程序執行完了初始化階段最后調用的componentDidMount函數之后,程序就開始正常的運行起來,此時就進入了存在階段。
存在階段執行流程:
程序在運行過程中,如果對this.state或this.props進行了修改,那么就會觸發存在階段的多個函數(調用流程如上圖所示)。
無論是修改this.state還是this.props,系統都會調用shouldComponentUpdate函數,判斷視圖是否需要渲染,如果不需要,則忽略本次狀態修改,回到運行狀態;如果需要,則在通過componentWillUpdate函數准備之后,重新調用render函數進行渲染。
注意:this.state使用的是狀態機機制,即將組件看成一個狀態機,一開始有一個初始狀態,然后在用戶互動的時候改變組件狀態,從而觸發重新渲染UI。
銷毀階段執行流程:
執行銷毀階段的情況有多種,如:當系統遇到錯誤而崩潰時;系統空間不足時;APP被用戶推出時,等等等等。
當遇到上述問題時,系統就會進入銷毀階段,這個階段只有一個過程:componentWillUnmount,這個方法用來清空一些無用內容,如:點擊事件的Listener等。
注意:銷毀階段是程序執行的出口,只要執行了銷毀階段,就表示程序已經自然或不自然的退出了。
狀態機
上面說到,在React Native生命周期初始化階段的getInitialState方法中用到了狀態機的原理,狀態機原理即通過修改程序中狀態機中的屬性的值,來達到改變界面顯示的目的。狀態機的一段示例代碼如下:
var BTouchableDemo = React.createClass({ getInitialState(){ return { content: '觸摸事件響應器' } }, render() { return ( <View style={styles.containerStyle}> <TouchableOpacity onPress={() => this.changeResultContent('點擊')} onPressIn={() => this.changeResultContent('按下')} onPressOut={() => this.changeResultContent('抬起')} onLongPress={() => this.changeResultContent('長按')}> <View style={styles.loginContainerStyle}> <Text style={styles.loginTextStyle}>TouchableOpacity</Text> </View> </TouchableOpacity> <Text style={styles.resultStyle}>{this.state.content}</Text> </View> ); }, changeResultContent(content) { this.setState({ content: content }); } });
可以看到,上面一段代碼通過修改狀態機中的content屬性,來修改底部的Text中的文本信息。
注意:如果是要獲取狀態機中的屬性值,則可以直接通過 this.state.* 的方式獲取;如果想要設置(更新)狀態機中某個屬性的值,則必須要通過 this.setState 方法設置。
ES 5和ES 6代碼的比較
ES 6的代碼風格相對於ES 5有很大的改變,實例化階段的幾個方法(render、getDefaultProps和getInitialState)在ES 5和ES 6的代碼差別很大。
使用ES 5的代碼編寫:
var CLifeCycle = React.createClass({ // 設置一些常量(程序中不可改變的量) getDefaultProps(){ return {name: 'Jack'}; }, // 設置狀態機屬性(程序中可以改變的量) getInitialState(){ return {age: 20}; }, // 渲染布局 render(){ return ( <View style={styles.containerStyle}> <Text style={styles.textStyle}>我是ES 5語法模板</Text> </View> ); } });
使用ES 6的代碼編寫:
export default class CLifeCycle extends Component { // 設置狀態機屬性(程序中可以改變的量) constructor(props) { super(props); this.state = {age: 20}; } // 渲染布局 render() { return ( <View style={styles.containerStyle}> <Text style={styles.textStyle}>我是ES 6語法模板</Text> </View> ); } } // 設置一些常量(程序中不可改變的量)的數據類型 CLifeCycle.propTypes = {name: React.PropTypes.string}; // 設置一些常量(程序中不可改變的量)的默認值 CLifeCycle.defaultProps = {name: 'Jack'};
獲取真實DOM節點
在React Native中,組件並不是真正的DOM節點,而是存在於內存中的一種數據結構,叫做虛擬DOM。只有當它插入文檔之后,才會稱為真正的DOM。
如果想要通過組件獲取真正的DOM節點,可以使用 ref 屬性設置標記,然后在需要使用的地方通過 this.refs.* 訪問這個組件。