1. 程式人生 > >React-Native開發總結-react-navigation應用與實踐

React-Native開發總結-react-navigation應用與實踐

最近更新時間:2017年10月29日14:03:20

    做開發的同學都知道,難的不是技術本身,而是產品需求的頻繁變更和邏輯複雜度,這讓研發工程師最為苦惱。但總體上來說,積累技術經驗,善於總結和記錄技術的實踐心得,也是一件優雅和愉悅的事情。

    對於開發的同學來說,RN開發原生APP的一項技術挑戰是,靈活自如的使用導航器跳轉頁面,對於常規需求的路由棧,入棧需求A>B>C>D + 出棧需求D>C>B>A,這個比較簡單,容易實現,但是真正的專案需求,一般沒有這麼簡單。比如:入棧需求是A>B>C>D,但出棧需求是D>A;入棧需求A>B>C>D ,出棧需求D>B>A,同時重新整理B(常規返回上級路由,不會重新整理上級頁面);因此,需要細心設計。

1、react-navigation官方文件

    請檢視官方文件 地址;

2、概述

    react-navigation是react和react native專案開發的路由控制元件,用於專案的導航設定,功能強大不可估量;

    三大部分:StactNavigator棧導航、TabNavigator標籤導航、DrawerNavigator抽屜導航-從左側滑出的介面;

3、棧導航常用的方法和方案

    A頁面跳轉到B頁面,入棧操作,不傳遞引數:

this.props.navigation.navigate('B');//A頁面沒有銷燬,componentWillUnMount方法沒有執行,如果從B頁面goBack回A頁面,A頁面的所有狀態都保持原樣;B頁面首次執行,依次執行constructor-componentWillMount-render-componentDidMount

    A頁面跳轉到B頁面,入棧操作,傳遞引數:

this.props.navigation.navigate('B',params);//params:{key01:value01,key02:value02...},按照ES6的語法,傳遞的鍵值對物件可以寫成{key01,key01};在B頁面拿到引數的方法,this.props.navigation.state.params.key01

    B頁面返回A頁面,出棧操作:

    this.props.navigation.goBack();//B頁面銷燬,componentWillUnMount方法執行;A頁面還是上一次離開時的狀態,並且沒有執行任何生命週期的方法;


    A頁面跳轉到B頁面,B頁面返回A頁面時,回撥傳參:

this.props.navigation.navigate('B',{callback:(data)=>{this.someFunc(data)}});

const {navigate,goBack,state} = this.props.navigation;

state.params.callback(data);

goBack();

4、高階技巧

    A>B>C>D>E>F,A到F都是入棧操作,從F回到B的方案: 

方案一:this.props.navigation.navigate

方案二:goBack

    預設,goBack()不傳遞任何引數,返回到上一個頁面,如果傳遞引數,需要傳遞頁面的key值;react-navigation對頁面棧的唯一標記資訊是this.props.navigation.state.key,因此可以採用這個屬性記錄頁面棧。如果有跨路由返回的需要,那麼在路由棧入棧時需要在this.props.navigation.state.params屬性中向下一層路由傳遞上層所有路由的路由標識key。(如果專案中有redux,則用redux儲存每個頁面的key值,更為方便)

    第一步,this.props.navigation.navigate('B',{keys:{A_previout:this.props.navigation.state.key}});

//A>B,首屏A進入B頁面,儲存首屏的this.props.navigation.state.key(Init-id-APP打包的時間戳-本次應用進入的累計次數(如果殺掉程序,首次進入是0,退出應用二次進入是1))資訊在路由資訊this.props.navigation.state.params.keys中,並命名為A_previout,由於this.props.navigation.goBack(key)中需要的的key是返回到指定頁面中下一個頁面的key

    第二步,this.props.navigation.navigate('C',{keys:{...this.props.navigation.state.params.keys,A:this.props.navigation.state.key}});//B>C

    第三步,this.props.navigation.navigate('D',{keys:{...this.props.navigation.state.params.keys,B:this.props.navigation.state.key}});//C>D

    第四步,this.props.navigation.goBack(this.props.navigation.state.params.A);//D>A

    可以看出來,上面的操作方案非常繁瑣,因此需要進行優化;goBack返回到指定頁面最簡單的操作是傳遞routeName,但需要改寫原始碼,將需要的key換成routeName;

把專案/node_modules/react-navigation/src/routers/StackRouter.js檔案裡的 
const backRoute = state.routes.find((route: *) => route.key === action.key); 
改成

const backRoute = state.routes.find(route => route.routeName === action.key);

    如果返回一級使用goBack(null),如果返回多級,使用goBack(routeName);

方案三:重置路由棧

import {NavigationActions} from 'react-navigation'

consot resetNavigationToRouteName = NavigationActions.reset({

index:1,

actions:[NavigationActions.navigate({routeName:'hp'}),NavigationActions.navigate({routeName:'secondPage'})]

})

this.props.navigation.dispatch(resetNavigationToRouteName);

    缺點,會一次性重新整理actions陣列中的所有頁面,即使頁面不是當前顯示的頁面;

5、入棧需求是A>B>C>D>E,出棧需求是E>B,並且重新整理B

6、入棧需求是A>B>E,出棧需求是E>C,並且銷燬E

7、自定義頁面跳轉動畫

    使用StackNavigator作為路由跳轉時,react-navigation元件提供了三種內建動畫:forHorizontal-入棧操作從右向左,出棧操作從左向右;forVerbical-入棧操作從下向上,出棧操作從上到下;forFadeFromBottomAndroid-入棧操作從底部淡出;

    通過傳遞引數讓頁面使用不同的跳轉動畫:

this.props.navigation.navigate('A',{transition:'forHorizontal'});

StackNavigator({

transitionConfig:()=>{

  screenInterpolator: (scenProps)=>{

const { scene } = sceneProps;
const { route } = scene;
const params = route.params || {};
const transition = params.transition || 'forHorizontal';
return CardStackStyleInterpolator[transition](sceneProps);

}

}

})

8、回退重新整理

    在採用react-native-navigation路由元件時,路由棧資訊是A>B>C>D,使用goBack()方法從D頁面到C頁面,D頁面執行了ComponentWillUnMount()方法,並且D頁面銷燬了,進入了C頁面時,C頁面的所有狀態都處於保持狀態,同時頁面並沒有重新整理,此時的需求,需要重新整理C頁面。

    實現方案一:使用DeviceEventEmitter

    實現方案二:使用this.props.navigation.state.params.callback(); goBack();

9、標籤導航

    標籤導航icon不顯示的問題,由於語法錯誤,es6的箭頭函式語法,函式體有兩種寫法,()和{},前者不用寫return直接返回內容,後者需要寫return才能返回內容;

tabBarIcon: () => (<Image resizeMode='contain' source={require('./src/img/cart-unselected.png')}/>)
tabBarIcon: () => {
return(
<Image resizeMode='contain' source={require('./src/img/cart-unselected.png')}/>
)
}

    標籤導航設定預設頁面後,進入到其他標籤頁,點選物理返回按鈕,應該退出應用,但返回到首頁的問題:

10、路由高階配置之:一級登陸頁面採用棧導航,二級頁面進入主程式採用標籤導航,三級頁面採用棧導航

    頁面頂層註冊棧導航:

import {StackNavigator} from 'react-navigation';

const RootStact = StackNavigator(RouteConfigs,StactNavigatorConfig);

    登入頁面跳轉採用NavigationActions,登陸成功的操作:

const resetAction = NavigationActions.reset({

index: 0,

actions: [NavigationActions.navigate({routeName:'Home'})]

});

this.props.navigation.dispatch(resetAction);

    進入程式主介面,是底部標籤導航的佈局:

import {TabNavigator} from 'react-navigation';

export default Home =TabNavigator(RouteConfigs,TabNavigatorConfig);

    主介面下面的二級頁面,退出程式主介面重新登陸的操作:

const resetAction =NavigationActions.reset({

index: 0,

actions: [NavigationActions.navigate({routeName:'Login'})]

});

this.props.navigation.dispatch(resetAction);

    這樣就完成了一個大型應用的基礎路由配置,在正式開發之前做好路由設計至關重要;

    備註:關於StackNavigator-RouteConfigs、StactNavigatorConfig、TabNavigator-RouteConfigs、TabNavigatorConfig這四項的配置方法和屬性設定,如果出現技術上的問題,可以隨時聯絡和諮詢我13161854871

11、路由高階配置之:一級頁面進入主程式採用標籤導航,二級頁面採用棧導航

    頁面頂層註冊標籤導航,二級頁面分別註冊棧導航,具體方案和步驟可參考上面

12、設定Title

    方案一:static navigationOptions = {header: null};

    方案二:

this.props.navigation.setOptions({
      headerTitle: '',
      headerTitleStyle:{},

      headerLeft: (<View></View>),
      headerRight: (<View></View>),
});

13、NavigationActions的弊端

    案例:頁面棧資訊A-B-C-D,在D頁面採用this.props.navigation.dispatch(resetAction);

const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'A'})
]
});

    這個方案很影響效能,因為dispatch之後ABC頁面都重新渲染後(觸發componentWillMount,render,componentDidMount),才返回到A頁面;

14、goBack()到指定頁面的快捷方案

    案例:頁面棧資訊A-B-C-D,在D頁面返回A頁面,執行下面方法3次即可,傳遞null是關鍵:

this.props.navigation.goBack(null);

    但還有個弊端,如果不清楚頁面棧內容到底有幾層,還是無法精確返回到指定頁面;

15、修改react-navigation元件原始碼,返回到指定頁面

    修改/node_modules/react-navigation/src/routers/StackRouter.js檔案286行程式碼為下面的內容:

if (action.type === NavigationActions.BACK) {
    let backRouteIndex = null;
    if (action.key) {
      const backRoute = state.routes.find(
        /* $FlowFixMe */
        /* 修改原始碼 */
        route => route.routeName === action.key
        /* (route: *) => route.key === action.key */
      );
      /* $FlowFixMe */
      console.log('backRoute =====',backRoute);
      backRouteIndex = state.routes.indexOf(backRoute);
      console.log('backRoute =====',backRouteIndex);
    }
    if (backRouteIndex == null) {
      return StateUtils.pop(state);
    }
    if (backRouteIndex >= 0) {
      return {
        ...state,
        routes: state.routes.slice(0, backRouteIndex+1),
        index: backRouteIndex - 1 + 1,
      };
    }
  }

    案例:頁面棧資訊A-B-C-D,在D頁面返回A頁面:

this.props.navigation.navigate('A');

    但如果返回到上層頁面必須使用this.props.navigation.goBack(null);

    同時,如果在滑動返回的時候,專案有可能會卡死;請注意使用該方法整合redux。

    參考https://www.jianshu.com/p/2f575cc35780

未完,待續...