前言 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 "HTML快速入門(一)" 學習 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝 文章第一版出自簡書,如果 ...
前言
學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 HTML快速入門(一) 學習
本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝
文章第一版出自簡書,如果出現圖片或頁面顯示問題,煩請轉至 簡書 查看 也希望喜歡的朋友可以點贊,謝謝
Navigator 與 NavigatorIOS 介紹
開發中,幾乎所有的APP中或多或少都會涉及到多個界面間的切換,在React Native中有兩個組件負責實現這樣的效果 —— Navigator 和 NavigatorIOS
Navigator可以在iOS和Android同時使用,而NavigatorIOS則是包裝了UIKit庫的導航功能,使用戶可以使用左劃功能來返回到上一界面
Navigator 屬性
官方文檔中是這樣解釋的:使用導航器可以讓你在應用的不同場景(頁面)間進行切換。導航器通過路由對象來分辨不同的場景。利用renderScene方法,導航欄可以根據指定的路由來渲染場景
可以通過configureScene屬性獲取指定路由對象的配置信息,從而改變場景的動畫或者手勢。查看Navigator.SceneConfigs來獲取預設的動畫和更多的場景配置選項
- configureScene:可選的函數,用來配置場景動畫和手勢。會帶有兩個參數調用(一個是當前的路由,一個是當前的路由棧)然後它會返回一個場景配置對象
- Navigator.SceneConfigs.PushFromRight(預設)
(route, routeStack) => Navigator.SceneConfigs.FloatFromRight
效果:
- Navigator.SceneConfigs.FloatFromLeft
(route, routeStack) => Navigator.SceneConfigs.FloatFromLeft
效果:
- Navigator.SceneConfigs.FloatFromBottom
(route, routeStack) => Navigator.SceneConfigs.FloatFromBottom
效果:
- Navigator.SceneConfigs.FloatFromBottomAndroid
(route, routeStack) => Navigator.SceneConfigs.FloatFromBottomAndroid
效果:
- Navigator.SceneConfigs.FadeAndroid
(route, routeStack) => Navigator.SceneConfigs.FadeAndroid
效果:
- Navigator.SceneConfigs.HorizontalSwipeJump
(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJump
效果:
- Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
(route, routeStack) => Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
效果:
- Navigator.SceneConfigs.VerticalUpSwipeJump
(route, routeStack) => Navigator.SceneConfigs.VerticalUpSwipeJump
效果:
- Navigator.SceneConfigs.VerticalDownSwipeJump
(route, routeStack) => Navigator.SceneConfigs.VerticalDownSwipeJump
效果:
- Navigator.SceneConfigs.PushFromRight(預設)
initialRoute:定義啟動時載入的路由。路由是導航欄用來識別渲染場景的一個對象。
initialRoute
必須是initialRouteStack(路由棧)
中的一個路由。initialRoute
預設為initialRouteStack
中最後一項initialRouteStack:提供一個路由集合用來初始化。如果沒有設置初始路由的話則必須設置該屬性。如果沒有提供該屬性,它將被預設設置成一個只含有
initialRoute
的數組naviagtionBar:可選參數,提供一個在場景切換的時候保持的導航欄
navigator:可選參數,提供從父導航器獲得的導航器對象
onDidFocus:每當導航切換完成或初始化之後,調用此回調,參數為新場景的路由
onWillFocus:會在導航切換之前調用,參數為目標路由器
renderScene:必要參數,用來渲染指定路由的場景。調用的參數是路由和導航器
(route, navigator) => <MySceneComponent title={route.title} navigator={navigator} />
sceneStyle:將會應用在每個場景的容器上的樣式
Navigator 方法
- 如果你得到一個
navigator對象
的引用,則可以調用許多方法來進行導航- getCurrentRoutes():獲取當前棧里的路由,也就是push進來,沒有pop掉的那些
- jumpBack():跳回之前的路由,當前前提是保留現在的,還可以再跳回來,會給你保留原樣
- jumpForward():上一個方法不是盜用之前的路由,用這個就可以跳回來了
- push(route):跳轉到新場景,並且將場景入棧,你可以稍後跳轉過去
- pop():跳轉回去並且卸載掉當前場景
- replace(route):用一個新的路由替換掉當前場景
- replaceAtIndex(route, index):替換掉指定序列的路由場景
- replacePrevious(route):替換掉之前的場景
- resetTO(route):跳轉到新場景,並且重置整個路由棧
- immediatelyResetRouteStack(routeStack):用新的路由數組來重置路由棧
- popToRoute(route):pop到路由指定的場景,在整個路由棧中,處於指定場景之後的場景將會被卸載
- popToTop():pop到棧中的第一個場景,卸載掉所有的其它場景
Navigator 使用
- 這邊我們先來完成一個最基本的導航控制器,然後慢慢深入,做一個完整的導航控制器
- 首先,我們先創建2個組件(home、Temp)並初始化組件,以供使用
- home組件代碼
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; var Home = React.createClass( { render() { return ( <View style={styles.container}> <Text>點擊跳轉</Text> </View> ); } }); var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1, justifyContent:'center', alignItems:'center' }, }); module.exports = Home;
- temp組件代碼
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; var Temp = React.createClass( { render() { return ( <View style={styles.container}> <Text>點擊返回</Text> </View> ); } }); var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1, justifyContent:'center', alignItems:'center' }, }); module.exports = Temp;
- home組件代碼
實例化
Navigator
需要2個必要的屬性 —— initialRoute 和 renderSence,它們的作用分別是告訴導航器需要渲染的場景、根據路由描述渲染出來<Navigator style={{flex: 1}} // 佈局 initialRoute={{ name:'Home', // 名稱 component:Home // 要跳轉的板塊 }} renderScene={(route, navigator) => { // 將板塊生成具體的組件 let Component = route.component; // 獲取路由內的板塊 return <Component {...route.params} navigator={navigator} /> // 根據板塊生成具體組件 }} />
- 實際上這樣我們的導航器就已經創建完畢了,但是從界面上我們看不到導航欄,但是已經具備導航功能,我們分別進到
home
和temp
文件中修改代碼,看看是否真的已經實現導航功能- home代碼
// 引入外部文件 var Temp = require('./Temp'); var Home = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp })}} > <Text>點擊跳轉</Text> </TouchableOpacity> </View> ); } });
- temp代碼
var Temp = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.pop()}} > <Text>臨時頁面</Text> </TouchableOpacity> </View> ); } });
效果:
- home代碼
在這裡,我們還可以為導航器設置轉場動畫,以滿足我們開發需求,官方已經提供了幾種常用的轉場動畫,這邊我們就取其中一種作示例,其它的效果可以參考上面的案例
<Navigator initialRoute={{ name:'Home', // 名稱 component:Home // 要跳轉的板塊 }} configureScene={(route) => { // 跳轉動畫 return Navigator.SceneConfigs.FloatFromBottom; }} style={{flex: 1}} renderScene={(route, navigator) => { // 將板塊生成具體的組件 let Component = route.component; // 獲取路由內的板塊 return <Component {...route.params} navigator={navigator} /> // 根據板塊生成具體組件 }} />
效果:
- 為了讓導航欄更加人性化,我們可以自己定製導航欄的樣式,定製方式和我們自定義界面一樣,只不過將按鈕的響應改為導航欄對應的功能即可,這邊就以最基本的導航欄樣式為例
- 視圖部分
var Home = React.createClass( { render() { return ( <View style={styles.container}> {/* 實例化導航欄 */} {this.setupNavBar()} </View> ); }, setupNavBar(){ return( <View style={styles.navBarStyle}> {/* 左邊按鈕 */} <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp })}} > <Text style={styles.leftButtonTitleStyle}>按鈕</Text> </TouchableOpacity> {/* 中間標題 */} <Text style={styles.navBarTitleStyle}>標題</Text> {/* 右邊按鈕 */} <TouchableOpacity> <Text style={styles.rightButtonTitleStyle}>按鈕</Text> </TouchableOpacity> </View> ) } });
- 樣式部分
var styles = StyleSheet.create({ container: { backgroundColor:'yellow', flex:1 }, navBarStyle: { // 尺寸 width:width, // 當前系統為iOS時,導航欄高度為64 height:Platform.OS === 'ios' ? 64 : 44, // 背景顏色 backgroundColor:'rgba(255, 255, 255, 0.9)', // 底部分隔線 borderBottomWidth:0.5, borderBottomColor:'gray', // 主軸方向 flexDirection:'row', // 對齊方式 alignItems:'center', justifyContent:'space-between', // 當前系統為iOS時,下次移動15 paddingTop:Platform.OS === 'ios' ? 15 : 0 }, leftButtonTitleStyle: { // 字體大小 fontSize:15, // 字體顏色 color:'blue', // 內邊距 paddingLeft:8 }, navBarTitleStyle: { // 字體大小 fontSize:17, // 字體顏色 color:'black' }, rightButtonTitleStyle: { // 字體大小 fontSize:15, // 字體顏色 color:'blue', // 內邊距 paddingRight:8 } });
iOS運行效果:
- 視圖部分
Android運行效果:
NavigatorIOS 屬性
barTintColor:導航條的背景顏色
barTintColor='red' // 導航欄背景顏色
效果:
initialRoute( {component: function, title: string, passProps: object, backButtonIcon: Image.propTypes.source, backButtonTitle: string, leftButtonIcon: Image.propTypes.source, leftButtonTitle: string, onLeftButtonPress: function, rightButtonIcon: Image.propTypes.source, rightButtonTitle: string, onRightButtonPress: function, wrapperStyle: [object Object]} ): 使用“路由”對象來包含要渲染的子視圖、它們的屬性、已經導航條配置。“push”和任何其他的導航函數的參數都是這樣的路由對象(下麵實例模塊會詳細講解)
itemWrapperStyle:導航器中的組件的預設屬性。一個常見的用途是設置所有頁面的背景顏色
navigationBarHidden:布爾值,決定導航欄是否隱藏
navigationBarHidden={true} // 隱藏導航欄
效果:
shadowHidden:布爾值,決定是否要隱藏1像素的陰影
shadowHidden={true} // 隱藏導航欄下麵的陰影
效果:
tintColor:導航欄上按鈕的顏色
tintColor='orange' // 按鈕的顏色
效果:
titleTextColor:導航器標題的文字顏色
titleTextColor='green' // 導航欄標題的文字顏色
效果:
translucent:布爾值,決定導航條是否半透明(註:當不半透明時頁面會向下移動導航欄等高的距離,以防止內容被遮蓋)
translucent={false} // 決定導航欄是否半透明(註:當不半透明時頁面會向下移動導航欄等高的距離,以防止內容被遮蓋)
效果:
interactivePopGestureEnabled:決定是否啟用滑動返回手勢。不指定此屬性時,手勢會根據
navigationBar
的顯隱情況決定是否啟用(顯示時啟用手勢,隱藏時禁用手勢),指定此屬性後,手勢與navigationBar
的顯隱情況無關interactivePopGestureEnabled={false} // 決定是否啟用滑動返回手勢
效果:
NavigatorIOS 方法
push(route):導航器跳轉到一個新的路由
pop():回到上一頁
popN():回到N頁之前。當 N=1 的時候,效果和 pop() 一樣
replace(route):替換當前頁的路由,並立即載入新路由的視圖
replacePrevious(route):替換上一頁的路由/視圖
replacePreviousAndPop(route):替換上一頁的路由/視圖並且立即切換回上一頁
resetTO(route):替換最頂級的路由並且回到它
replaceAtIndex:替換指定路由
popToRoute(route):一直回到某個指定的路由
popToTop():回到最頂層的路由
NavigatorIOS 使用
- 先來看看怎麼使用
NavigatorIOS
,我們需要給他指定一個路由,這樣它才能知道顯示哪個頁面 - 和
Navigator
一樣NavigatorIOS
需要有個根視圖來完成初始化,所以我們需要先創建一個組件來描述這個界面,並將這個組件通過路由的形式告訴NavigatorIOS
,這樣就可以將這個界面展示出來- 首先,創建一個
Home
組件,用來作為NavigatorIOS
的根視圖- 視圖部分
var Home = React.createClass( { render() { return ( <View style={styles.container}> <Text>點擊跳轉頁面</Text> </View> ); } });
- 樣式部分
var styles = StyleSheet.create({ container: { // 背景顏色 backgroundColor:'yellow', flex:1, // 對齊方式 justifyContent:'center', alignItems:'center' }, });
- 視圖部分
- 接著我們在
index.ios.js
內獲得Home
文件
// 引用外部文件 var Home = require('./home');
- 然後我們實例化一個
NavigatorIOS
並設置路由
var navigatorDemo = React.createClass( { render() { return ( <NavigatorIOS initialRoute={{ component: Home, // 要跳轉的頁面 title:'首頁' // 跳轉頁面導航欄標題 }} style={{flex:1}} // 此項不設置,創建的導航控制器只能看見導航條而看不到界面 /> ); } });
效果:
- 首先,創建一個
- 這樣我們就完成了基本的導航控制器了,那麼怎麼進行跳轉呢?其實也很簡單,官方提供的方法內有多種供我們選擇(具體參考上面的方法一欄)
- 這邊我們就來實現最簡單的跳轉和返回,我們使用
TouchableOpacity
讓Home
中的
var Home = React.createClass( { render() { return ( <View style={styles.container}> <TouchableOpacity onPress={() => {this.props.navigator.push({ component:Temp, // 需要跳轉的頁面 title:'跳轉的界面' // 跳轉頁面導航欄標題 })}} > <Text>點擊跳轉頁面</Text> </TouchableOpacity> </View> ); } });
效果:
- 這邊我們就來實現最簡單的跳轉和返回,我們使用
這邊順便來看下導航欄左右兩邊的按鈕怎麼設置,並且響應點擊事件
initialRoute={{ component: Home, // 要跳轉的頁面 title:'首頁', // 跳轉頁面導航欄標題 leftButtonTitle:'左邊', // 實例化左邊按鈕 onLeftButtonPress:() => {alert('左邊')}, // 左邊按鈕點擊事件 rightButtonTitle:'右邊', // 實例化右邊按鈕 onRightButtonPress:() => {alert('右邊')} // 右邊按鈕點擊事件 }}
效果:
當然圖片設置的方式也是一樣的,只需要調用
leftButtonIcon
和 'rightButtonIcon` 即可(和TabBarIOS一樣,只支持本地圖片)
補充
- props:組件中的props是一種父級向子級傳遞數據的方式
- this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是
this.props.children
屬性。它表示組件的所有子節點 - 它裡面包含了所有的屬性,所以上面我們在別的文件中可以通過
this.props.navigator
的方式獲取navigator
- this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是
- state:
state
是React中組件的一個對象.React把用戶界面當做是狀態機,想象它有不同的狀態然後渲染這些狀態,可以輕鬆讓用戶界面與數據保持一致。React中,更新組件的state
,會導致重新渲染用戶界面(不要操作DOM).簡單來說,就是用戶界面會隨著state
變化而變化- 原理:常用的通知React數據變化的方法是調用
setState(data,callback)
.這個方法會合併data到this.state
,並重新渲染組件.渲染完成後,調用可選的。callback
回調.大部分情況不需要提供callback
,因為React會負責吧界面更新到最新狀態 - 哪些組件應該有 state
- 大部分組件的工作應該是從
props
里取數據並渲染出來.但是,有時需要對用戶輸入,伺服器請求或者時間變化等作出響應,這時才需要state
- 組件應該儘可能的無狀態化,這樣能隔離state,把它放到最合理的地方(Redux做的就是這個事情?),也能減少冗餘並易於解釋程式運作過程
- 常用的模式就是創建多個只負責渲染數據的無狀態(stateless)組件,在他們的上層創建一個有狀態(stateful)組件並把它的狀態通過props
- 傳給子級.有狀態的組件封裝了所有的用戶交互邏輯,而這些無狀態組件只負責聲明式地渲染數據
- 大部分組件的工作應該是從
- 哪些應該作為 state
state
應該包括那些可能被組件的事件處理器改變並觸發用戶界面更新的數據.這中數據一般很小且能被JSON序列化.當創建一個狀態化的組件的時候,應該保持數據的精簡,然後存入this.state
。 在render()中在根據state
來計算需要的其他數據.因為如果在state里添加冗餘數據或計算所得數據,經常需要手動保持數據同步
- 哪些不應該作為 state
this.state
應該僅包括能表示用戶界面狀態所需要的最少數據.因此,不應該包括:- 計算所得數據:
- React組件:在render()里使用props和state來創建它
- 基於props的重覆數據:儘可能保持用props來做作為唯一的數據來源.把props保存到state中的有效的場景是需要知道它以前的值得時候,因為未來的props可能會變化
- 計算所得數據:
- 原理:常用的通知React數據變化的方法是調用