React Native,是一個混合移動應用開發框架,本文根據 C. Enrique Ortiz 的文章 Dissecting React Native 同時結合本身開發中的一些理解編寫html
React Native 是目前流行的跨平臺移動應用開發框架之一。本文概述了 React Native,以及做者在開發 React Native 應用的最佳實踐經驗雜談。node
在開發移動應用時,咱們能夠選擇使用原生,純基於 Web 或使用混合方法(即便用原生和基於 Web 的技術的組合)。可使用 Apache Cordova(或 Adobe PhoneGap)等開源平臺編寫 HTML,CSS 和 JavaScript 的跨平臺混合應用。這些應用在原生 Web 視圖(瀏覽器)中運行,因此看着就像 Web 應用,不過可使用 JavaScript 和一些 Apache Cordova 插件調用原生功能和 API。因此可使用相似於 Web 應用而非原生應用的用戶界面上架到 Apple Store 或 Google Play 的應用。或者,可使用 C#,XAML 和 CSS 使用 Xamarin 來編寫原生應用。對於用 HTML5,CSS 和 JavaScript 編寫的非原生移動 Web 應用,還能夠配套使用 jQuery Mobile 等框架。react
React Native 採用不一樣的方法進行混合移動應用開發。React,React Native 採用 JavaScript 代替生成原生 UI 組件的方式來構建基於 Web 的交互界面,所以會有更豐富的 UI 體驗效果,同時也可以很好地調用底層框架的UI使用。 React Native 已經成爲一種流行的移動開發技術,它提供了一個使用 JavaScript 構建原生跨平臺移動應用的強大框架,在須要時,咱們也可使用 Objective-C,Swift或 Java 來編寫原生代碼來橋接。android
使用 React Native 有利有弊。從積極的方面來講,React Native 已經成爲受支持的開源社區的熱門,可使用一組技術(如 JSX,React Native 組件和 JavaScript)爲 iOS 和 Android 構建移動應用。這反過來有助於在發佈新版本時使 iOS 和 Android 應用保持同步。可是,React Native 仍在不斷髮展,當要在當前不須要 React Native 提供的功能(例如如何進行UI導航或使用地圖)時決定使用哪些庫時,可能會形成混淆。此外,根據應用的複雜程度,可能須要編寫區別於平臺的代碼來解決移動平臺的差別。對於複雜的應用,可能要編寫自定義組件或深刻了解 iOS 和 Android(例如,出於性能緣由或將 React Native 添加到現有原生應用時所需的但不支持的UI組件)。ios
React Native 的核心語言是 JavaScript,特別是 ECMAScript 6(ES6)。所以,須要熟悉ES6的一些最新功能。此外,在開始開發第一個 React Native 應用以前,須要安裝必備的工具,好比 IDE,JavaScript 庫等。另外,還須要瞭解核心的 React Native API。git
ES6 引入了許多改進,其中一些值得一提,由於它們在最新的 React Native 框架中已經被用到:es6
沒有指定參數的箭頭函數:github
() => { ... }
複製代碼
帶一個參數,注意省略了的括號:數據庫
x => { ... }
複製代碼
指定幾個參數:npm
(x, y) => {*
...
}
複製代碼
參數定義和箭頭函數的箭頭必須在同一行中。
function myAsyncFunc() {
return new Promise(
function (resolve, reject) {
:
resolve(result);
:
if (error) {
reject(error);
}
});
}
複製代碼
比較之前的異步函數:
myAsyncFunc().then(result =\> { ··· }).catch(error =\> { ··· });
複製代碼
這只是新功能的一個示例,但還有許多其餘功能。關於ES6的一個很好的在線參考是 Axel Rauschmayer 的 Exploring ES6。
接下來,咱們來看看如何設置開發平臺和環境。
目前來看,開發 iOS 原生應用只能用 macOS 系統。因爲大多數移動應用都會涉及到 Android 和 iOS兩端,所以應該考慮使用 Mac 來開發 React Native 移動應用。
在建立項目以前,你須要安裝一堆東西,主要包括:
Android Studio和Xcode工具:安裝最新版本以構建和發佈應用。對於 Android 開發,確保爲要運行的Android API 版本配置正確的模擬器。
JavaScript IDE或文本編輯器:不須要使用 Xcode 或 Android Studio 來編寫 JavaScript 代碼,真正須要的只是一個文本編輯器。好比使用 Atom(由 GitHub 開發),Sublime Text 或喜歡的任何優秀文本編輯器,筆者目前用的是 VSC。
JavaScript 或 React Native 包:安裝一些工具來管理 React Native 軟件包以及三方庫,包括:
sudo npm install -g create-react-native-app
下載經常使用的第三方庫。 React Nativ 提供了不少基礎組件,與原生 API 使用相比會更加簡單。常規的移動應用使用可能包括有相機,管理狀態,並具備導航,地圖,圖標和複選框。這都均可以從 React Native 社區獲取對用組件:
此外,能夠到React Native Elements獲取須要的其餘UI組件元素。在開發一個完整功能的 app 時,須要用到上面提到的 UI 組件。用這個方法來安裝之前的 packages :
npm install _package-name_ --save
React Native Debugger:這是一個很是棒的調試器,同時它仍是一個獨立的應用,裏面能用到的工具包括 React Inspector 和 Redux DevTools。配置好環境後能夠鏈接到應用(注意:一次只能調適一個應用),能夠很方便的實時查看 React Native 應用的狀態,調適必備工具,如圖所示:
如今,來創建一個簡單應用並運行一下:
1.建立工程。用如下語句來建立,指定建立的工程路徑,默認建立在當前路徑下:
create-react-native-app [APPNAME]
cd [APPNAME]
複製代碼
注意:這一步建立了一個沒有進行任何配置的 React Native app 模板。在 GitHub 項目中閱讀有關 create-react-native-app 的更多信息。
安裝完成後,可使用 npm 執行如下命令來運行應用:
2.啓動 app。經過運行 npm run android 和 npm run ios 在模擬器上運行應用。這將在開發模式下啓動應用。同時還會啓動 Metro bundler,能夠在修改代碼後實時更新應用(在調試移動應用時這很棒)。
若是運行成功,會有這樣的提示:
咱們簡單快速瀏覽一下 React Native API ,從 React 轉變過來的 React Native ,它繼承了 JSX,state,props 和組件生命週期的概念。而後,它經過提供對原生UI組件和功能的支持來擴展 React。這是經過導入 React 和 React Native 功能來實現的,如清單1所示:
清單1.從 React 和 React Native 導入
import React, { Component } from **'react'**
import { View, ScrollView, StyleSheet } from **'react-native**'
複製代碼
導入後,咱們就能夠訪問 React Native 對應的組件,這些組件支持咱們所需的許多經常使用UI組件:
還有一點須要注意,React Native 還提供了不少原來不支持的功能。好比使用地圖,甚至是圖標和複選框。雖然底層操做系統支持,但 React Native 仍是須要安裝第三方庫。也就是說,React Native(包括開發工具)所支持的任何複雜或不受支持的內容都要求咱們編寫其餘代碼或使用第三方庫來承載。
常規的的 React Native 應用由許多組件組成,如圖所示:
在一個應用容器中,每一個界面會包含一個或多個 View(和樣式),界面跳轉導航,狀態管理和數據模型。還有本地存儲和網絡請求功能。最後但並不是最不重要的是,有不少第三方庫能夠擴展應用的功能。
如下介紹部分圖中內容:
應用容器中包含多個 screen 和其餘組件的集合。應用自己由許多 screen 組成,每一個 screen 包含多個視圖,如按鈕,文本輸入和列表。View 是構建用戶界面的最基本組件,映射到 iOS 和 Android 的原生 View 組件。
清單2是一個界面示例,包含一個列表,使用 ScrollView 實現,以及一個封裝在 Checkbox UI 組件中的任務列表:
# components/TaskList/TaskList.js
import React, { Component } from 'react'
import { View, ScrollView, StyleSheet } from 'react-native'
import { CheckBox } from 'react-native-elements'
import styles from './TaskListStyle; export default class TaskList extends Component { renderTaskItem = (item, idx) => { return ( <View style={styles.row}> <Checkbox checked = {item.checked} title = {item.text} onPress = { () => onPressItem(item, idx) } /> </View> ); } render() { const {list} = this.props return ( <ScrollView style={styles.container}> {list.map( this.renderTaskItem )} </ScrollView> ) } } 複製代碼
代碼中列表組件須要導入各類依賴項,包括 React Native 中未找到的第三方Checkbox UI組件,能夠從 React Native Elements下載該組件。
在清單2中,UI組件和樣式是分開的。咱們能夠建立更高級UI組件並與樣式隔離開。咱們把全部組件放到一個子目錄中,每一個組件都有其 component.js 和 style.js 文件,如清單3所示:
./Components
+-TaskList
+-TaskList.js
+-TaskListStyle.js
複製代碼
或者,使用更通用的方法:
+-TaskList
+-index.js
+-style.js
複製代碼
清單4顯示了一個樣式文件的示例:
# components/TaskList/TaskListStyle.js
import { StyleSheet } from 'react-native';
// Define the styles
export default StyleSheet.create({
container: {
flex: 1,
paddingLeft: 20,
},
row: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-start',
},
});
複製代碼
樣式須要使用 StyleSheet 組件建立,它相似於 CSS 樣式表。能夠查看 individual UI component 獲取更多樣式屬性。
React Native 提供了一個 Dimensions 組件來檢測屏幕尺寸:
import { Dimensions } from 'react-native';
var {height, width} = Dimensions.get('window');
複製代碼
Dimensions 可根據須要實時調整界面UI;例如,咱們能夠根據須要的應用樣式,在方向更改時調整UI,包括改變橫向與縱向方向的文本輸入字段的位置和長度。
另外咱們還可能須要處理另外一種狀況的UI適配。好比須要實現 Androi d或 iOS 特有的行爲的狀況。包括與兩端系統相關的差別,例如UI控件的呈現及使用。要解決此設計問題,咱們須要使用不一樣的邏輯或樣式,或者在更復雜的狀況下,實現自定義組件。所以 React Native 提供了不一樣的方法來檢測系統,以即可以以編程方式決定要執行的操做:使用 Platform 組件甚至使用特定於平臺的文件擴展名。
使用Platform 組件檢測系統會返回「ios」或「android」。例如:
import {Platform} from 'react-native';
if (Platform.OS === 'ios') {
}
if (Platform.OS === 'android') {
}
複製代碼
Platform 還提供 Platform.select 和 Platform.Version,分別用於檢測平臺和平臺版本。有關更多信息,請參閱平臺文檔。應該僅對較小的更改使用 Platform.O S和 Platform.select。不然,咱們將使用大量硬編碼的 if 或 switch 語句,這會使寫的代碼更難以遵循。
若是要編寫更復雜的代碼或組件,則應使用特定於平臺的文件擴展名。在這種方法中,使用每一個平臺文件拆分代碼。也就是說,若是咱們的代碼不能跨平臺公用,好比咱們的任務列表,那麼將編寫兩個文件,一個用於 iOS,一個用於Android,而後讓 React Native 選擇對應的文件:
./Components
+-TaskList
+-TaskList.io.js
+-TaskList.android.js
+-TaskListStyle.ios.js
+-TaskListStyle.android.js
複製代碼
而後,讓React Native選擇正確的文件:
const TaskList = require('./Components/TaskList/TaskList); 複製代碼
本文未介紹 iOS 和 Android 之間究竟有什麼不一樣,但要注意的事項包括渲染和常規組件或UI控件使用佈局。如今,若是事實證實 Android 與 iOS 應用的佈局設計在主要方面有所不一樣,咱們能夠分別使用 index.ios.js 和 index.android.js 文件而不是 index.js 來控制主應用。這就能夠徹底控制不一樣平臺的應用佈局和流程。爲了最大限度地減小重複代碼,須要將其從新打包爲可重用的程序。
React Native 中的導航是一個具備挑戰性的領域,由於 React Native 提供的導航要麼不夠強大,要麼僅針對 iOS。相反,有大量社區項目試圖解決跨平臺導航問題,其中一些項目比其餘項目更受歡迎。這裏只是重點闡述當下流行的 React Native Navigation。
React Native Navigation 提供了一套很是完整的 API 來註冊界面組件,包括含有 tab 的應用,以及啓動單個界面應用。同時它還提供了其餘 API 來管理模態跳轉和導航跳轉,以及用於處理堆棧,處理按鈕,可見性以及自定義導航器自己的低級 API。
安裝最新穩定版本的 react-native-navigation(運行 npm install react-native-navigation --save)並按照其網站上的 Android 和 iOS 安裝說明進行操做
另外必須經過調用 registerComponent()將惟一名稱的 Navigator 註冊到全部 screen 組件。註冊後,就能運行包含 Tab 的應用或單個 screen 應用,如清單8所示:
import { Navigation } from "react-native-navigation";
import MainScreen from "./src/screens/MainScreen/MainScreen";
:
// register the MainScreen component
Navigation.registerComponent(
'MainScreen',
() => 'MainScreen'
);
:
:
// Start the (single screen)App
export default () => Navigation.startSingleScreenApp({
screen: {
screen: "MainScreen",
title: "Main Screen"
}
});
複製代碼
若是是基於 tab 的使用,則須要建立更多的 screen ,由於必須指明不一樣的 tab 及其關聯的 screen ,tab 詳細信息和樣式,例如:
// Start the (tab-based) App
export default () => Navigation.startTabBasedApp({
tabs: [
{
screen: "screen unique name",
label: "label",
title: "title",
icon: icons[0],
navigatorButtons: {
leftButtons: [
{
icon: icons[2],
title: "title",
id: "unique id"
}
]
}
},
{
screen: "screen unique name",
label: "label",
title: "title",
icon: icons[1],
navigatorButtons: {
leftButtons: [
{
icon: icons[2],
title: "title",
id: "unique id"
}
]
}
}
],
tabsStyle: {
tabBarSelectedButtonColor: "black"
},
drawer: {
left: {
screen: "drawer screen unique name"
}
},
appStyle: {
tabBarSelectedButtonColor: "red"
},
});
複製代碼
能夠根據須要經過調用相應的功能在單個和基於 tab 的 screen 之間切換。另外,請注意前一個示例中抽屜的使用。能夠爲單個 screen 應用和基於 tab 的應用定義抽屜側面菜單。
在 React(和 React Native)中,props 和 state 用於控制組件。 props(properties)是用於在建立組件時自定義組件的參數。例如,Button 組件爲許多屬性提供支持;在清單10中,title 和 onPress 是 props。
<Button
onPress={onPressHandler}
title="Learn More"
:
/>
複製代碼
屬性由 React 自身設置(由父組件設置),並在組件的整個生命週期內保持固定。 State 是應用內的特殊數據,可根據須要進行更改以驅動組件行爲,好比刷新數據。state 應該在構造函數中初始化,而且只能經過調用 setState 來更改。清單11顯示了 state 的用法:
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {isActive: true};
}
toggleActiveState() {
this.setState(previousState => {
return { isActive: !previousState.isActive };
});
}
render() {
:
}
}
複製代碼
清單11是一個簡單 state 管理的示例,但若是應用對於許多 screen 組件和組件間狀態管理更復雜,則應該利用 Redux。 Redux 是一種更先進的 state 管理方法,它提供了一個實現 state 管理及相關操做和處理程序的框架。可是使用 Redux 會增長應用的複雜性,所以應該考慮僅在真正須要它時使用它(即,對於較大的複雜應用)。
React Native 使用 AsyncStorage 爲持久化存儲提供支持,AsyncStorage 是一個易於使用的基於鍵值的異步(持久)存儲系統,對整個應用而言是全局的。它很簡單,由於它不須要過多的設置。在 iOS 上,它表現爲一系列字典或者是文件。在Android 上,它基於 SQLite 或 RocksDB:
import {AsyncStorage} from 'react-native'
:
const USERNAMES_KEY = 'Usernames'
const user = {
name: 'John',
last: 'Smith',
username: 'jsmith'
}
// Storing the item
AsyncStorage.setItem(USERNAMES_KEY, JSON.stringify(user))
:
// Get item promise to retrieve the item
AsyncStorage.getItem(USERNAMES_KEY).then((item) => {
const userItem = JSON.parse(item)
})
:
複製代碼
可是 AsyncStorage 有許多限制,例如性能低下,沒有索引,也沒有加密。讓我再說一遍:它沒有加密。若是要存儲大量數據,性能和加密是應用的重要因素,則應考慮使用 Realm React Native 等替代方案。 Realm React Native 是跨平臺的,性能更好,支持加密,甚至還帶有版本的 ListView(具備與 React Native ListView 相同的 API 簽名),它在 Realm 之上進行了優化,大大改善了對本地存儲的訪問。 Realm 使用 static schema 的途徑來提升性能。(能夠看一下筆者的這篇文章來學習 Realm 的使用,傳送門->移動數據庫 Realm 在 React-Native 的使用詳解 ):
class Usernames {
static schema = {
name: 'User',
properties: {
name: 'string',
last: 'string',
username: 'string',
}
}
}
let realm = new Realm({schema: [Usernames]});
realm.write(() => {
realm.create('Usernames', { name: 'John', last: "Smit", username : "jsmith" });
});
複製代碼
移動應用一般是須要網絡鏈接的應用。移動應用鏈接到網絡以進行身份驗證。 React Native 提供不一樣的網絡 API:
清單14使用 fetch API 從服務器獲取用戶數據的 json 文件:
{
"users": [
{ "name": "John Smith", "username": "jsmith"},
{ "name": "Matt People", "username": "mpeople"},
{ "name": "Graciela Lopez", "username": "glopez"},
{ "name": "Jeff Bezos", "username": "jbezos"},
]
}
--
import React from 'react';
import { View, Text, FlatList, ActivityIndicator} from 'react-native';
export default class FetchUsernames extends React.Component {
constructor(props){
super(props);
}
// dispatch the fetch once the component mounts
componentDidMount(){
return fetch('https://...')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.users,
}, function(){
});
})
.catch((error) => {
console.error(error);
});
}
// render the component, populating the FlatList from the
// state.dataSource that was in turn populated from the JSON
render(){
return(
<View>
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<Text>{item.name}, {item.username}</Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
複製代碼
關於 fetch API 和網絡的一些注意事項:
本文僅是對 React Native 提供了一個高層次的觀點見解。 React Native 是一個很複雜的話題。若是要深究,則須要更詳細地研究本文所涵蓋的每一個領域。
React Native 是一個充滿活力且不斷髮展的框架和社區。但 React Native 並不像原生應用開發那樣成熟。因此必須爲Android,iOS 和 React Native 的開發保持穩定的環境,包括全部相關的開發工具。對 於React Native 中不存在的組件,能夠 Google 搜索,就能夠找到所需內容,請記住所選的第三方庫可能會更改或維護可能會中止或被放棄,這是一個要考慮的主要風險。
React Native 並不適合全部人或每一個項目。這須要考慮建立和維護 React Native 代碼,工具,開發環境和技能集的時間,精力和成本,而不是僅僅關注原生代碼。去年也有大型開發組織的例子,例如 Airbnb,它們在 React Native 上投入了大量的精力和投入,但最終決定迴歸純粹的原生開發。
- 韋譽翔:廣州蘆葦科技 APP 團隊 iOS 開發工程師
- 咱們正在招募小夥伴,有興趣的小夥伴能夠把簡歷發到 app@talkmoney.cn,備註:來自掘金社區
- 詳情能夠戳這裏--> 廣州蘆葦信息科技