React Native 是一款使用JavaScript和React編寫(xiě)原生移動(dòng)應(yīng)用的框架,熟練使用React的開(kāi)發(fā)者可以很容易的使用React Native來(lái)開(kāi)發(fā)APP,本文介紹一下React Native的安裝和基本使用。
安裝
本文是在window環(huán)境下安裝的,其他環(huán)境請(qǐng)參考官方文檔。
1. 安裝必需軟件
- Node (版本需大于等于 12)
- Python2 (版本必須為 2.x(不支持 3.x))
- JDK (版本必須是 1.8)
2. 安裝nrm,yarn
npm install nrm -g // 安裝nrm
npx nrm use taobao // 切換淘寶源
npm install yarn -g // 安裝yarn
3. 安裝Android Studio
- 下載安裝Android Studio
- 安裝過(guò)程中Custom選項(xiàng)先不勾選
- 安裝完成后在 Android Studio 的歡迎界面中點(diǎn)擊"Configure",然后點(diǎn)擊SDK Manager
- 在 SDK Manager 中選擇"SDK Platforms"選項(xiàng)卡,然后在右下角勾選"Show Package Details"。展開(kāi)Android 9 (Pie)選項(xiàng),確保勾選了下面這些組件:
然后點(diǎn)擊"SDK Tools"選項(xiàng)卡,同樣勾中右下角的"Show Package Details"。展開(kāi)"Android SDK Build-Tools"選項(xiàng),確保選中了 React Native 所必須的28.0.3版本。你可以同時(shí)安裝多個(gè)其他版本。
配置 ANDROID_HOME 環(huán)境變量
React Native 需要通過(guò)環(huán)境變量來(lái)了解你的 Android SDK 裝在什么路徑,從而正常進(jìn)行編譯。
打開(kāi)控制面板 -> 系統(tǒng)和安全 -> 系統(tǒng) -> 高級(jí)系統(tǒng)設(shè)置 -> 高級(jí) -> 環(huán)境變量 -> 新建,創(chuàng)建一個(gè)名為ANDROID_HOME的環(huán)境變量(系統(tǒng)或用戶變量均可),指向你的 Android SDK 所在的目錄(具體的路徑可能和下圖不一致,請(qǐng)自行確認(rèn))

SDK 默認(rèn)是安裝在下面的目錄:
c:\Users\你的用戶名\AppData\Local\Android\Sdk
你可以在 Android Studio 的"Preferences"菜單中查看 SDK 的真實(shí)路徑,具體是Appearance & Behavior → System Settings → Android SDK。
- 把一些工具目錄添加到環(huán)境變量 Path 中
打開(kāi)控制面板 -> 系統(tǒng)和安全 -> 系統(tǒng) -> 高級(jí)系統(tǒng)設(shè)置 -> 高級(jí) -> 環(huán)境變量,選中Path變量,然后點(diǎn)擊編輯。點(diǎn)擊新建然后把這些工具目錄路徑添加進(jìn)去:platform-tools、emulator、tools、tools/bin
%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\emulator
%ANDROID_HOME%\tools
%ANDROID_HOME%\tools\bin
4. 創(chuàng)建項(xiàng)目
找到你需要?jiǎng)?chuàng)建項(xiàng)目的目錄運(yùn)行
npx react-native init myApp
Windows 用戶請(qǐng)注意,請(qǐng)不要在某些權(quán)限敏感的目錄例如 System32 目錄中 init 項(xiàng)目!會(huì)有各種權(quán)限限制導(dǎo)致不能運(yùn)行!
5. 下載木木模擬器
6. 由于第一次運(yùn)行時(shí)需要下載大量編譯依賴,這里建議使用阿里云的maven鏡像
修改 android/build.gradle
allprojects {
repositories {
mavenLocal()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
....
}
7. 連接模擬器
木木模擬器默認(rèn)端口為 7555
項(xiàng)目根目錄運(yùn)行
adb connect 127.0.0.1:7555
連接成功會(huì)有提示
8. 運(yùn)行項(xiàng)目
在你的項(xiàng)目目錄中運(yùn)行yarn android 或者yarn react-native run-android
第一次運(yùn)行耗時(shí)較長(zhǎng)
基本使用
入門Hello World
編輯App.js
import React, { Component } from 'react';
import { Text, View } from 'react-native';
export default class HelloWorldApp extends Component {
render() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Hello, world!</Text>
</View>
);
}
}
React Native 看起來(lái)很像 React,只不過(guò)其基礎(chǔ)組件是原生組件而非 web 組件。
組件
一個(gè) App 的最終界面,其實(shí)也就是各式各樣的組件的組合。組件本身結(jié)構(gòu)可以非常簡(jiǎn)單——唯一必須的就是在render方法中返回一些用于渲染結(jié)構(gòu)的 JSX 語(yǔ)句。
- 核心組件
| 原生組件 | 安卓 | IOS | HTML | 描述 |
|---|---|---|---|---|
| <View> | <ViewGroup> | <UIView> | 非滾動(dòng)<div> | 一個(gè)支持Flexbox,樣式,一些觸摸處理和輔助功能控件布局的容器 |
| <Text> | <TextView> | <UITextView> | <p> | 顯示,設(shè)置樣式和嵌套文本字符串,甚至處理觸摸事件 |
| <Image> | <ImageView> | <UIImageView> | <img> | 顯示不同類型的圖像 |
| <ScrollView> | <ScrollView> | <UIScrollView> | <div> | 通用滾動(dòng)容器,可以包含多個(gè)組件和視圖 |
| <TextInput> | <EditText> | <UITextField> | <input> | 允許用戶輸入文本 |
- 文本輸入框組件
TextInput是一個(gè)允許用戶輸入文本的基礎(chǔ)組件。它有一個(gè)名為onChangeText的屬性,此屬性接受一個(gè)函數(shù),而此函數(shù)會(huì)在文本變化時(shí)被調(diào)用。另外還有一個(gè)名為onSubmitEditing的屬性,會(huì)在文本被提交后(用戶按下軟鍵盤(pán)上的提交鍵)調(diào)用。
- 觸摸組件(Button)
<Button onPress={()=>alert("你好世界")} title="確定" color="#f30"></Button>
其中onPress是回調(diào)函數(shù),title是按鈕上顯示的文字,color是按鈕顏色;
也可以作為警告框,擁有多個(gè)選項(xiàng),從而彈出不同內(nèi)容:
<Button
onPress={()=>Alert.alert("小胖別吃了好嗎?","你已經(jīng)160斤了!",[
{text:'不行',onPress:()=>alert("小胖:不行我餓")},
{text:'好吧',onPress:()=>alert("小胖:好吧我不吃了")},
{text:'難過(guò)',onPress:()=>alert("小胖:我是單身狗,胖點(diǎn)怕啥")},
])}
title="詢問(wèn)小胖"
color="blue">
</Button>
可以看到我們?cè)O(shè)置了三個(gè)選項(xiàng),每一項(xiàng)都會(huì)彈出不同的內(nèi)容,內(nèi)容也是自己設(shè)置的;
- 觸摸組件(Touchable 系列組件)
這個(gè)組件的樣式是固定的。所以如果它的外觀并不怎么搭配你的設(shè)計(jì),那就需要使用TouchableOpacity或是TouchableNativeFeedback組件來(lái)定制自己所需要的按鈕
使用TouchableHighlight來(lái)制作按鈕或者鏈接。注意此組件的背景會(huì)在用戶手指按下時(shí)變暗。
在 Android 上還可以使用TouchableNativeFeedback,它會(huì)在用戶手指按下時(shí)形成類似墨水漣漪的視覺(jué)效果。
TouchableOpacity會(huì)在用戶手指按下時(shí)降低按鈕的透明度,而不會(huì)改變背景的顏色。
如果想在處理點(diǎn)擊事件的同時(shí)不顯示任何視覺(jué)反饋,則需要使用TouchableWithoutFeedback
某些場(chǎng)景中可能需要檢測(cè)用戶是否進(jìn)行了長(zhǎng)按操作。可以在上面列出的任意組件中使用onLongPress屬性來(lái)實(shí)現(xiàn)。
- 滾動(dòng)視圖
ScrollView是一個(gè)通用的可滾動(dòng)的容器,可以在其中放入多個(gè)組件和視圖,而且這些組件并不需要是同類型的。ScrollView 不僅可以垂直滾動(dòng),還能水平滾動(dòng)(通過(guò)horizontal屬性來(lái)設(shè)置)。
ScrollView適合于顯示數(shù)量不多的滾動(dòng)元素。放置在ScrollView中的所有組件都會(huì)被渲染
- 長(zhǎng)列表
FlatList組件用于顯示一個(gè)垂直的滾動(dòng)列表,其中的元素之間的結(jié)構(gòu)近似而僅數(shù)據(jù)不同。
FlatList和ScrollView不同的是,F(xiàn)latList并不立即渲染所有元素,而是優(yōu)先渲染屏幕上可見(jiàn)的元素。
FlatList組件必須的兩個(gè)屬性是data和renderItem。data是列表的數(shù)據(jù)源,而renderItem則從數(shù)據(jù)源中逐個(gè)解析數(shù)據(jù),然后返回一個(gè)設(shè)置好格式的組件來(lái)渲染。
例如渲染數(shù)據(jù):
<FlatList
keyExtractor={item=>item.ID}
data={this.state.movies}
numColumns={3} //一行顯示3個(gè)
columnWrapperStyle={styles.row} //增加下方寫(xiě)的樣式
renderItem={({item})=>{return(
<View style={{marginTop:20}} key={item.ID}>
<Image
style={{width:135,height:160}}
source={{uri:item.defaultImage}}>
</Image>
<Text style={{marginTop:5,textAlign:"center"}}>{item.MovieName}</Text>
</View>
)}}
/>
/*
flatList長(zhǎng)列表組件,
keyExtractor是key抽取
data數(shù)據(jù)指定
numColumns行的數(shù)量,
renderItem列的渲染
columnWrapperStyle行的樣式指定
*/
更多組件請(qǐng)參考官方文檔
Props
render() {
let pic = {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
};
return (
<Image source={pic} style={{width: 193, height: 110}} />
);
}
{pic}外圍有一層括號(hào),我們需要用括號(hào)來(lái)把pic這個(gè)變量嵌入到 JSX 語(yǔ)句中。括號(hào)的意思是括號(hào)內(nèi)部為一個(gè) js 變量或表達(dá)式,需要執(zhí)行后取值。
自定義的組件也可以使用props。通過(guò)在不同的場(chǎng)景使用不同的屬性定制,可以盡量提高自定義組件的復(fù)用范疇。只需在render函數(shù)中引用this.props,然后按需處理即可。下面是一個(gè)例子:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
class Greeting extends Component {
render() {
return (
<View style={{alignItems: 'center', marginTop: 50}}>
<Text>Hello {this.props.name}!</Text>
</View>
);
}
}
export default class LotsOfGreetings extends Component {
render() {
return (
<View style={{alignItems: 'center'}}>
<Greeting name='Rexxar' />
<Greeting name='Jaina' />
<Greeting name='Valeera' />
</View>
);
}
}
最終3個(gè)名字會(huì)被依次輸出。
State
React Native使用兩種數(shù)據(jù)來(lái)控制一個(gè)組件:props和state。props是在父組件中指定,而且一經(jīng)指定,在被指定的組件的生命周期中則不再改變。對(duì)于需要改變的數(shù)據(jù),我們需要使用state。
一般來(lái)說(shuō),需要在class中聲明一個(gè)state對(duì)象,然后在需要修改時(shí)調(diào)用setState方法。典型的場(chǎng)景是在接收到服務(wù)器返回的新數(shù)據(jù),或者在用戶輸入數(shù)據(jù)之后。你也可以使用一些“狀態(tài)容器”比如Redux來(lái)統(tǒng)一管理數(shù)據(jù)流。
每次調(diào)用setState時(shí),BlinkApp 都會(huì)重新執(zhí)行 render 方法重新渲染。
注意
- 一切界面變化都是狀態(tài)state變化
- state的修改必須通過(guò)setState()方法
- this.state.likes = 100; // 這樣的直接賦值修改無(wú)效!
- setState 是一個(gè) merge 合并操作,只修改指定屬性,不影響其他屬性
- setState 是異步操作,修改不會(huì)馬上生效
樣式的書(shū)寫(xiě)
行內(nèi)樣式
<View>
<Text style={[styles.red,styles.big]}>我是內(nèi)聯(lián)樣式文本</Text>
<Text style={{fontWeight:"bold"}}>我是行內(nèi)樣式文本</Text>
<Text style={{...styles.big,color:"yellow", fontStyle:"italic"
}}>我是內(nèi)聯(lián)+行內(nèi)混合樣式文本</Text>
</View>
在render函數(shù)外部定義樣式
const styles = StyleSheet.create({
center:{justifyContent:"center",alignItems:"center"},
row:{flexDirection:"row",justifyContent:"space-around"},
col:{flex:1,height:180},
big:{fontSize:48},
red:{color:"#f30"}
});
網(wǎng)絡(luò)請(qǐng)求
一般長(zhǎng)列表的數(shù)據(jù)都是通過(guò)網(wǎng)絡(luò)請(qǐng)求獲取的;然后渲染到頁(yè)面;
傳統(tǒng)方式如下:
fetch('https://mywebsite.com/endpoint/', {
method: 'POST', //請(qǐng)求方式
headers: {
'Content-Type': 'application/x-www-form-urlencoded', //請(qǐng)求頭
},
body: 'key1=value1&key2=value2', //請(qǐng)求參數(shù)
});
上面的示例演示了如何發(fā)起請(qǐng)求。很多情況下,還需要處理服務(wù)器回復(fù)的數(shù)據(jù)。網(wǎng)絡(luò)請(qǐng)求天然是一種異步操作;異步的意思是應(yīng)該提取方法會(huì)返回一個(gè)Promise,這種模式可以簡(jiǎn)化異步風(fēng)格的代碼;
如下所示:
function getMoviesFromApiAsync() { //獲取電影xx的方法,自己創(chuàng)建的
return fetch('https://facebook.github.io/react-native/movies.json') //接口網(wǎng)站
.then((response) => response.json()) //將請(qǐng)求到的數(shù)據(jù)轉(zhuǎn)為json格式
.then((responseJson) => {
return responseJson.movies; //請(qǐng)求成功返回?cái)?shù)據(jù)
})
.catch((error) => {
console.error(error); //請(qǐng)求失敗,輸出錯(cuò)誤
});
}