初學React Native,如果沒有人指引,會發現好多東西無從下手,但當有人指引後,會發現其實很簡單。這也是本人寫這篇博客的主要原因,希望能幫到初學者。 本文不會介紹如何搭建開發環境,如果你還沒有搭建,可參考這裡的官方文檔:https://react-native.org/doc/getting- ...
初學React Native,如果沒有人指引,會發現好多東西無從下手,但當有人指引後,會發現其實很簡單。這也是本人寫這篇博客的主要原因,希望能幫到初學者。
本文不會介紹如何搭建開發環境,如果你還沒有搭建,可參考這裡的官方文檔:https://react-native.org/doc/getting-started.html 。
本文也不會介紹各種組件,太多了,可參考這裡的官方文檔:https://react-native.org/doc/components-and-apis.html 。
本文將會從創建項目開始,到基本的常見佈局(tabs)、多頁面之間的導航,加入熱更新、用戶行為分析功能。總之,我希望告訴你在實際工作中一個React Native App是如何開發出來的。
每一個知識點不會展開來詳細講解,這篇博客只是起到一個指引的作用,能讓你少走一點彎路,少一些自己摸索的時間。
第一步:創建項目
當然,前提是你已經搭建好了開發環境。
在命令行中,進入你想要放項目文件的地方,我放在D盤的mydocs文件夾下(D:\mydocs\),執行以下命令:
react-native init 項目名
我的項目名是“test0”,所以完整的命令應該是這樣的:
react-native init test0
由於需要到外網下載文件,而我朝對網路是有管制的,因此這個命令可能需要執行較長時間。只要網路沒有中斷,沒有報錯,就耐心等待吧。
待命令執行完畢後,在 D:\mydocs\ 目錄下就多了一個 test0 文件夾。裡面有很多已經預設創建好的文件和文件夾。
先來對預設創建的項目文件做個簡單的認識。
用你喜歡的任意編輯器(我喜歡用VS Code)找開這個文件夾。你將看到一個類似這樣的目錄結構:
其中,
/package.json 是包管理的配置文件,要安裝什麼包,可在這裡配置,項目的基本信息,比如項目名、版本號、項目說明、等等,也可在此配置。但大多數情況下,可以不用管它。
/index.js 項目的啟動文件。
/App.js 首頁文件,在/index.js中會載入這個文件。
/node_modules 項目中用到的所有包都存放在這個文件夾中。自己的項目文件不要放在這裡。
/android 這裡放的是與Android原生編譯相關的一些文件,作為一名React Native開發者,一般情況下也不用去管它。
/ios 這裡放的是與iOS原生編譯相關的一些文件,作為一名React Native開發者,一般情況下也不用去管它。
這裡需說明一下,如果你的項目的開發中,需要大量去動 /android 和 /ios 下的代碼,甚至在裡面加入很多業務邏輯,那就說明你的項目開發是存在問題的,一般情況下,只有在某些與編譯、配置、發佈相關的才會動到這裡的代碼。
第二步:創建我們的第一個頁面-Hello React Native
其實,現在我們已經可以運行項目了。 react-native init test0 這個命令已經預設為我們創建了一個首頁,但我想替換為我自己的內容。
修改 /App.js ,有以下的代碼替換掉原來的所有代碼:
1 import React from 'react'; 2 import { SafeAreaView, View, Text } from 'react-native'; 3 4 class App extends React.Component { 5 render() { 6 return ( 7 <SafeAreaView> 8 <View> 9 <Text>Hello React Native</Text> 10 </View> 11 </SafeAreaView> 12 ); 13 } 14 }; 15 16 export default App;
預設生成的代碼是hooks語法,但我不喜歡,我更喜歡用class。我覺得class的結構更清晰一些。
第三步:在模擬器中查看運行效果
我們的第一個頁面已經創建好了,現在需要查看一下運行效果。可用真機調試,但大多數情況下,用模擬器會更方便一些。
有各種模擬器可供選擇,我喜歡用Android Studio自帶的模擬器。安裝方法同樣見這個文檔:https://react-native.org/doc/getting-started.html 。
打開Android Studio,點擊 Configure -> AVD Manager
在打開的視窗中,就能看到所有你已經創建過的模擬器了。如果你還沒有創建過模擬器,就點擊 Create Virtual Device 創建一個。下麵是我已經創建好的模擬器。
點擊後面的綠色三角形,就能啟動模擬器了,啟動後是這個樣子的:
再次回到我們的項目。在命令行中,進入項目文件夾( D:\mydocs\test0\ ),執行以下命令:
react-native run-android
因為我用的是Windows系統,就不演示ios的運行效果了。ios的運行,需在Mac電腦上執行以下命令:
react-native run-ios
同樣,可能需要等待比較長的時間,因為同樣需要到外網下載編譯工具。以後再執行此命令時就會快很多了。(這就是很多碼農們恨透了GFW的主要原因之一 ^_^)
以下是目前我們的代碼所運行的效果:
第四步:多個頁面之間的導航
現在我們的示例只有一個頁面,如果有多個頁面,又應該怎樣從一個頁面跳轉到另一個頁面呢?
在React Native中,有很多包可以實現此功能。比如:React Navigation、React Native Navigation、等等。
網上有各種文章比較過各種包之間的好壞,但對於一般的應用,其實差別不大。雖然在項目中我一般都使用React Native Navigation,但相對來說,React Navigation使用起來更加簡單、比較容易上手。因此在這個示例中,我選擇使用React Navigation。
先創建第二個頁面。
在項目的根目錄下創建一個文件: /one.js 。並加入以下代碼:
1 import React from 'react'; 2 import { View, Text } from 'react-native'; 3 4 class One extends React.Component { 5 render() { 6 return ( 8 <View> 9 <Text>另一個頁面</Text> 10 </View> 12 ); 13 } 14 }; 15 16 export default One;
然後在 /App.js 中加個按鈕,希望點擊它後能跳轉到 /one.js 。將 /App.js 的代碼修改為下麵這樣子:
1 import React from 'react'; 2 import { SafeAreaView, View, Text, Button } from 'react-native'; 3 4 class App extends React.Component { 5 onPress() { 6 // TODO: 跳到另一個頁面 7 } 8 9 render() { 10 return ( 11 <SafeAreaView> 12 <View> 13 <Text>Hello React Native</Text> 14 </View> 15 <View> 16 <Button title="點擊我去另一個頁面" onPress={this.onPress}></Button> 17 </View> 18 </SafeAreaView> 19 ); 20 } 21 }; 22 23 export default App;
接下來需要在 onPress() 方法中加入跳轉的代碼。在這之前,需要先安裝React Navigation包。
在項目的根目錄下執行以下命令(你可能需要先關閉模擬器中打開的App,或者直接關閉模擬器,並結束項目的運行):
npm install @react-navigation/native
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
npm install @react-navigation/stack
然後將 /App.js 中的代碼修改為下麵這樣子:
1 import React from 'react'; 2 import { View, Text, Button } from 'react-native'; 3 import { NavigationContainer } from '@react-navigation/native'; 4 import { createStackNavigator } from '@react-navigation/stack'; 5 import One from './one'; 6 7 const Stack = createStackNavigator(); 8 9 class Home extends React.Component { 10 onPress = () => { 11 this.props.navigation.navigate('One'); 12 } 13 14 render() { 15 return ( 16 <View> 17 <View> 18 <Text>Hello React Native</Text> 19 </View> 20 <View> 21 <Button title="點擊我去另一個頁面" onPress={this.onPress}></Button> 22 </View> 23 </View> 24 ); 25 } 26 }; 27 28 class App extends React.Component { 29 render() { 30 return ( 31 <NavigationContainer> 32 <Stack.Navigator initialRouteName="Home"> 33 <Stack.Screen name="Home" component={Home} /> 34 <Stack.Screen name="One" component={One} /> 35 </Stack.Navigator> 36 </NavigationContainer> 37 ); 38 } 39 } 40 41 export default App;
這次的改動比較大。新加了一個 class Home ,將之前 class App 中的代碼移到了 class Home 中,現在的 class App 是一個維護導航的容器。
留意 class Home 中 onPress() 內的代碼,在這裡,用 navigation.navigate(name) 跳轉到另一個頁面。通過 class App 中的處理, class Home 的 props 中有了一個 navigation 對象。
再次執行 react-native run-android 在模擬器中查看效果:
點擊頁面中的Button,就能跳轉到 /one.js 了。
在 /one.js 中,可以直接點擊左上角的“返回”圖標回到上一頁。但為了演示如何使用代碼返回到上一頁,我將 /one.js 的代碼修改為下麵這樣子:
1 import React from 'react'; 2 import { View, Text, Button } from 'react-native'; 3 4 class One extends React.Component { 5 onPress = () => { 6 this.props.navigation.goBack(); 7 } 8 9 render() { 10 return ( 11 <View> 12 <Text>另一個頁面</Text> 13 <View> 14 <Button title="返回" onPress={this.onPress}></Button> 15 </View> 16 </View> 17 ); 18 } 19 }; 20 21 export default One;
在這裡使用了 navigation.goBack() 返回到上一頁。
現在,點擊 /one.js 中的“返回”Button,就能回到上一頁了。
navigation 還有個 push() 方法,也是較常用的。你可以試試效果。具體使用方法參考官方文檔:https://react-native.org/doc/navigation.html 。
第五步:加入選項卡tabs
在App中,比較常見的一種佈局是在底部有一排選項卡tabs。
其實,tabs是由多個頁面組成的,因此,在tabs之間切換,也是在多個頁面之間導航。因此這裡同樣需要用到React Navigation。
在項目的根目錄下執行以下命令(同樣,你可能需要先停止項目的運行):
npm install @react-navigation/bottom-tabs
為了更好的演示效果,加入第三個頁面 /two.js ,代碼如下:
1 import React from 'react'; 2 import { View, Text } from 'react-native'; 3 4 class Two extends React.Component { 5 render() { 6 return ( 7 <View> 8 <Text>第三個頁面</Text> 9 </View> 10 ); 11 } 12 }; 13 14 export default Two;
在 /App.js 中引入它:
1 import Two from './two';
將之前的
1 import { createStackNavigator } from '@react-navigation/stack';
替換為:
1 import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
並將之前的
1 const Stack = createStackNavigator();
替換為:
1 const Tab = createBottomTabNavigator();
然後將 class App 修改為:
1 class App extends React.Component { 2 render() { 3 return ( 4 <NavigationContainer> 5 <Tab.Navigator> 6 <Tab.Screen name="Home" component={Home} options={{title: 'Javascript'}} /> 7 <Tab.Screen name="One" component={One} options={{title: 'Python'}} /> 8 <Tab.Screen name="Two" component={Two} options={{title: 'PHP'}} /> 9 </Tab.Navigator> 10 </NavigationContainer> 11 ); 12 } 13 }
再次在項目根目錄下執行 react-native run-android 查看運行效果:
可以看到在底部出現了一排選項卡tabs。點擊它就能在不同頁面之間切換了。
當然,還可以給tabs加上Icon圖標。這裡就不演示了,懶得去找圖標了。更多內容可參考官方文檔:https://react-native.org/doc/navigation.html 。
第六步:加入熱更新功能
好吧,假設到這裡我們App的功能已經開發完了。
但就可以這樣結束了嗎?
當然不行。
我們老大說過:用React Native做App,卻不做熱更新,那你用React Native幹嘛?
這當然是句玩笑話,但實際情況確實如此,只要是用React Native開發App,一般都會加入熱更新功能,這是用React Native開發App的最大優勢之一。如果將最大的優勢都丟棄了,確實說不過去。
在這裡我使用CodePush中國提供的熱更新服務。使用的方法比較簡單,官方的示例文檔已經寫得很清楚了,我就不加說明瞭,直接上代碼。
官方示例文檔在這裡:http://code-push.cn/docs/1600.htm 。
按照官方文檔安裝好 cpcn-react-native 後,在 /App.js 文件中引入它:
1 import cpcn from "cpcn-react-native";
為了偷懶,我將官網示例中的代碼直接複製到 class Home 中:
1 class Home extends React.Component { 2 constructor(props) { 3 super(props); 4 this.state = { 5 upgradeState: 0, 6 upgradeAllBytes: 0, 7 upgradeReceived: 0 8 }; 9 } 10 11 onPress = () => { 12 this.props.navigation.navigate('One'); 13 } 14 15 componentDidMount() { 16 cpcn.check({ 17 // 檢查是否有新版本後調用此方法 18 checkCallback: (remotePackage, agreeContinueFun) => { 19 if(remotePackage){ 20 // 如果 remotePackage 有值,表示有新版本可更新。 21 // 將 this.state.upgradeState 的值設為1,以顯示提示消息 22 this.setState({ 23 upgradeState: 1 24 }); 25 } 26 }, 27 // 下載新版本時調用此方法 28 downloadProgressCallback: (downloadProgress) => { 29 // 更新顯示的下載進度中的數值 30 this.setState({ 31 upgradeReceived: downloadProgress.receivedBytes, 32 upgradeAllBytes: downloadProgress.totalBytes 33 }); 34 }, 35 // 安裝新版本後調用此方法 36 installedCallback: (restartFun) => { 37 // 新版本安裝成功了,將 this.state.upgradeState 的值設為0,以關閉對話框 38 this.setState({ 39 upgradeState: 0 40 }, () => { 41 // 調用此方法重啟App,重啟後將會使用新版本 42 restartFun(true); 43 }); 44 } 45 }); 46 } 47 48 upgradeContinue = () => { 49 // 用戶確定更新後,調用此方法以開始更新 50 cpcn.agreeContinue(true); 51 // 將 this.state.upgradeState 的值設為2,以顯示下載進度 52 this.setState({ 53 upgradeState: 2 54 }); 55 } 56 57 render() { 58 return ( 59 <> 60 <View> 61 <View> 62 <Text>Hello React Native</Text> 63 </View> 64 <View> 65 <Button title="點擊我去另一個頁面" onPress={this.onPress}></Button> 66 </View> 67 </View> 68 <Modal 69 visible={this.state.upgradeState > 0} 70 transparent={true}> 71 <View style={{padding:18, backgroundColor:"rgba(10,10,10,0.6)", height:"100%", display:"flex", flexDirection:"row", alignItems:"center"}}> 72 <View style={{backgroundColor:"#fff", width:"100%", padding:18}}> 73 { 74 this.state.upgradeState == 1 75 && 76 <View> 77 <View style={{paddingBottom:20}}> 78 <Text>發現新版本</Text> 79 </View> 80 <View> 81 <Button title="馬上更新" onPress={this.upgradeContinue}/> 82 </View> 83 </View> 84 } 85 { 86 this.state.upgradeState == 2 87 && 88 <View> 89 <Text style={{textAlign:"center"}}>{this.state.upgradeReceived} / {this.state.upgradeAllBytes}</Text> 90 </View> 91 } 92 </View> 93 </View> 94 </Modal> 95 </> 96 ); 97 } 98 };
這就算搞定了。這個App已經有了熱更新功能。
第七步:加入用戶行為分析功能
這還不算完。在真實開發工作中,一個App上線之後,還需要做的事情還很多,比如需要將錯誤日誌傳到伺服器,以方便監控是否存在Bug。再比如,很重要的一點,需要分析用戶的行為,以方便對產品進行改進。這是公司在運營App時的很重要的參考數據。
之前我們都是自己寫用戶行為分析。但是搞得代碼很亂,每次修改和加新的監控都很麻煩,並且我們自己的分析功能寫得也不是很好。
前段時間偶然發現,CodePush中已經有了用戶行為分析的功能,於是便向老大提議用這個,結果還受到了老大的表揚。^_^
官方文檔在這裡:http://code-push.cn/docs/1700.htm 。
接下來我在這個示例項目中加入用戶行為分析。我需要知道我的用戶對哪些內容感興趣,是Javascript,還是Python?又或者是PHP?
只需為每個頁面做個埋點,看用戶訪問哪個比較多就知道結果了。
由於之前在做熱更新時已經引入了 cpcn-react-native ,所以不需重覆引入。但需要註意的是, cpcn-react-nataive 必須在引入所有組件之前引入,例如我現在的引入順序是這樣子的:
1 import React from 'react'; 2 import cpcn from "cpcn-react-native"; 3 import { View, Text, Button, Modal } from 'react-native'; 4 import { NavigationContainer } from '@react-navigation/native'; 5 import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 6 import One from './one'; 7 import Two from './two';
然後在 import 語句之後加入以下代碼:
1 cpcn.useFootprint();
再然後修改 /App.js 中的 class Home ,在它的 constructor 中加入埋點:
1 class Home extends React.Component { 2 constructor(props) { 3 super(props); 4 this.footprint('Javascript'); 5 // 其它代碼。。。。 6 } 7 // 其它代碼。。。。 8 }
給 /one.js /two.js 也加入埋點:
1 class One extends React.Component { 2 constructor(props) { 3 super(props); 4 this.footprint('Python'); 5 } 6 // 其它代碼。。。。 7 }
1 class Two extends React.Component { 2 constructor(props) { 3 super(props); 4 this.footprint('PHP'); 5 } 6 // 其它代碼。。。。 7 }
在 /one.js 中有個Button,我也希望能監控用戶有沒有點擊它。因此給該Button也加個埋點:
1 <Button footprint="點了返回按鈕" title="返回" onPress={this.onPress}></Button>
然後就可以去CodePush的控制臺中查看分析報表了。下麵是我的測試結果:
第八步:現在總應該結束了吧?
結束了嗎?
不,還沒有。^_^
比如在上面已經提到過的,在真實的開發工作中,我們還需要記錄用戶的崩潰日誌。再比如,我們需要知道我們的用戶主要分佈在哪些地方。再比如,我們需要將某些數據做緩存。再比如,我們需要在伺服器發生錯誤時給用戶一個友好的提示。等等等等。。。。。
總之,在實際的開發工作中,開發一個App,比在培訓班做個項目要做的事情多得多。
第九步:結束了
雖然如上面所說,還需要做的事情還很多,但在這篇博客里我就不寫了。以後有時間再寫吧。
之前曾對自己說,要多寫點博客,即可記錄一些知識點,也可幫到別人。但卻一直沒有做到。下班之後就真的不想再動了。^_^
這是第一次寫這麼長的博客,希望能幫到看這篇博客的你。