React Native
使你只使用JavaScript也能編寫原生移動應用Android
和iOS
。 想必前端跨平臺都據說過如今如今比較火熱的Flutter
、ReactNative
、Weex
。Flutter
它多是使用了dart
語言也是跨端,而後是一種新的語法可能對前端來講得從新學習一遍。而ReactNative
與Weex
它兩自己就是經過前端React
與Vue
語法,只要會這兩個這兩個框架仍是很好上手的。ReactNative
它在設計原理上和React一致,經過聲明式的組件機制來搭建豐富多彩的用戶界面。稍微區別的就是style
樣式的寫法稍微有點區別感受剩下的都是跟React
一致。css
由於是跨端項目須要安卓環境以及iOS
的開發環境,目前我本身就只是搭建了安卓的環境。前端
必須安裝的依賴有:Node、JDK
和 Android Studio
。 這裏有關Node
以及JDK
和安卓的的環境搭建就不一一介紹了,網上都是有的,官網也是有的。node
(這裏注意的是node
的版本以及jdk
的版本還有就是安卓中SDK
的版本)react
node >=12
jdk 1.8 (必須)
Android SDK Platform 29
官網ReactNative安卓環境搭建(官方文檔): reactnative.cn/docs/enviro…ios
經過node
安裝帶的npx
初始化項目git
npx react-native init AwesomeProject
複製代碼
這裏的AwesomeProject
就是項目的名稱, 也能夠經過react-native-cli
初始化項目,可是這個必須是全局安裝了react-native-cli
,感受是仍是npx
來的方便因此我也是選擇這個。這個咱們默認初始化react-native
的最高版版本,咱們也能夠經過--version X.XX.X
來指定項目react-native
的版本。github
還有就是咱們能夠經過--template
來指定下載項目初始化的指定模板,好比官網有示列寫的使用typescript
的版本web
npx react-native init AwesomeTSProject --template react-native-template-typescript
複製代碼
由於到接觸到react-native
開發APP項目本身也是開發好幾個,可是每次新建項目都是經過複製以前的項目的依賴來作項目的初始化感受很麻煩因此本身也手動根據本身須要基礎依賴來搭建了一個初始化的模板。typescript
npm包地址:www.npmjs.com/package/rea…npm
github地址:github.com/jetBn/react…
該模板主要是配置了一些eslint
和一些項目基本依賴配置信息,好比使用Ant
的react-native
的UI框架以及網絡請求,上傳文件等等。 具體依賴以及文件目錄信息以下:
項目主要入口是index.js
可是咱們頁面有不少頁面,因此項目中使用React Navigation
承接頁面中tabbar
以及子頁面等等。
React Navigation
(官方文檔): reactnavigation.org/
建立TabBar主要是是用React Navigation
中@react-navigation/bottom-tabs
這個包官方文檔中都是有詳細的建立說明,因此這裏也不列舉說明了,我相信這個文檔仍是難不倒咱們的,雖然文檔是英文的。
在此記錄下自定義tabBar
import React, { useEffect, useState } from 'react'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' // 引入建立底部Tab主包
const Tab = createBottomTabNavigator() // 獲取組件
const MyTabBar=({ state, descriptors, navigation }) => {
const focusedOptions = descriptors[state.routes[state.index].key].options
if (focusedOptions.tabBarVisible === false) {
return null
}
return (
// 主要tabbar版塊佈局
<View
style={{
// overflow: 'hidden',
height: calc(50, 'height'),
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderTopColor: '#F4F4F4',
backgroundColor: '#FFFFFF',
borderTopWidth: 1,
paddingBottom: isIPhoneX() ? 16 : 0, // 判斷是否爲isIPhoneX
position: 'relative'
}}>
// 這裏咱們獲取我在Tab.Navigator中定義的Tab.Screen循環出來
{state.routes.map((route, index) => {
const { options } = descriptors[route.key]
// console.log(options.tabBarIcon())
//這裏判斷咱們是否有些tabBarLabel沒有寫就是用title再沒有就是用route的name
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name
const isFocused = state.index === index //判斷是不是是選中的tab
//點擊事件
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key
})
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name)
}
}
//長按事件
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key
})
}
// 返回每一項經過TouchableOpacity
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
key={index}
style={{
// flex: ,
width: calc(150),
justifyContent: 'center',
alignItems: 'center'
}}>
{options.tabBarIcon({ focused: isFocused })}
<Text
style={{
color: isFocused ? '#222222' : '#777777',
fontSize: sT(11),
textAlign: 'center',
fontWeight: '500',
marginTop: calc(6)
}}>
{label}
</Text>
</TouchableOpacity>
)
})}
// 因爲上面是咱們定義佈局flex這裏我定義咱們自定義的圖標定位在中間
<View
style={{
width: calc(83),
height: calc(83),
position: 'absolute',
left: '50%',
marginLeft: -calc(41),
bottom: calc(5)
}}>
<TouchableOpacity
activeOpacity={1}
onPress={() => {
getToken().then((res) => {
if (res === '') {
navigation.navigate('Login')
} else {
showImgPikcer()
}
})
}}>
<Image
style={{
width: '100%',
height: '100%'
}}
source={require('../../assets/images/home_tab_scanning.png')}
/>
</TouchableOpacity>
</View>
</View>
)
}
const Tabs = () => {
return (
<Tab.Navigator
tabBar={(props) => <MyTabBar {...props} />} //將tabBar中主要的pops傳入我自定義的方法
tabBarOptions={{
labelStyle: { bottom: 5 },
keyboardHidesTabBar: true,
allowFontScaling: false
}}>
<Tab.Screen
name="Home"
component={Home}
options={{
tabBarLabel: '首頁',
title: '首頁',
tabBarVisible: isShowTabbar,
tabBarIcon: ({ focused }) => {
if (focused) {
return (
<Image
source={require('../../assets/images/home_tab_file_s.png')}
/>
)
} else {
return (
<Image
source={require('../../assets/images/home_tab_file_n.png')}
/>
)
}
}
}}
/>
<Tab.Screen
name="Mine"
component={Mine}
options={{
tabBarLabel: '個人',
tabBarIcon: ({ focused }) => {
if (focused) {
return (
<Image
source={require('../../assets/images/home_tab_user_s.png')}
/>
)
} else {
return (
<Image
source={require('../../assets/images/home_tab_user_n.png')}
/>
)
}
}
}}
/>
</Tab.Navigator>
)
}
export default Tabs
複製代碼
在此咱們定義的tab.js
文件只需在個人模板中router
文件夾下的index.js
中路由棧中引入使用就能夠了,效果圖以下圖。
基本的頁面建立方式是在index.js
中引入@react-navigation/stack
包這裏我使用分出來文件的形式在main
文件夾下寫咱們全部的頁面文件配置。
這裏配置了webview
的頁面在screen
的props
配置中我能夠配置頁面的信息好比標題欄顏色,標題欄左側,右側顯示等等,也能夠自定義標題欄。
在項目中我主要使用axios
做爲網絡請求的封裝,具體主要實現網絡請求以及響應的攔截器。主要是實現了網絡請求中參數使用qs
封裝變成form表單請求數據,以及響應結果中全局判斷是否正常返回數據。
const service = axios.create({
baseURL: BASE_URL, // api 的 base_url
timeout: 60000, // 請求超時時間
header: {
'Content-Type':'application/x-www-form-urlencoded',
}
})
// request攔截器
service.interceptors.request.use(
async config => {
if(config.method === 'post') {
config.data = Qs.stringify(config.data)
}
config.headers["token"] = await getToken()
// 獲取網絡的狀態
NetInfo.fetch().then(state => {
if(!state.isConnected) {
Toast.show('網絡鏈接失敗', {
duration: Toast.durations.LONG,
position: 0,
shadow: true,
animation: true,
hideOnPress: true,
delay: 1000
})
}
});
console.log(config.headers)
return config
},
error => {
console.log('error', error)
Promise.reject(error)
}
)
// response 攔截器
service.interceptors.response.use(
response => {
console.log(response.data.status)
if(typeof response.data.status !=='undefined' && response.data.status !== 200000500){
return response.data.data
}
if(typeof response.data ==='object' && typeof response.data.status === 'undefined') {
return response.data
}
// console.log(response.status)
Toast.show(response.data.message)
return Promise.reject(response.data)
},
error => {
if (error.response.status == 401) {
}
return Promise.reject(error)
}
)
export default service
複製代碼
由於我這邊網絡請求用的是axios
當時文件上傳一下就懵了,不知道該怎麼弄,畢竟手機上跟網頁上仍是有差異的。由於axios
也是在網頁上使用的。由於在網頁上選擇的文件都是使用blob
上傳的。而我在手機上選擇的文件都是file
開頭的文件路徑或者就直接返回文件路徑。並且後端的api
也是經過網頁中上傳文件的方式接收對應文件來進行上傳。
當時就想這怎麼轉成跟前端網頁的同樣形式,最後仍是也是找到了一個fetch
封裝的包rn-fetch-blob
,它支持文件上傳下載。
github地址:github.com/joltup/rn-f…
我本身經過Promise
的方式封裝了圖片文件上傳等方法
因爲使用React Navigation
因此我也是直接使用官方提供的react-native-appearance
包中提供的hook
方法(useColorScheme
),而且使用此包中提供AppearanceProvider
包裹總體的路由組件。
config
配置信息在config文件下,而後對應dark
與light
模式下顏色配置,而後我須要經過useColorScheme
中獲取的值直接對應這個Object
的字段key
就能夠了,配置文件以下頁面中使用
2. 手動設置與自動設置配置,若是當咱們設置了自動跟隨系統還好,就直接經過這個hook
咱們就能獲取對應的顏色信息了,可是若是用戶設置了手動那咱們又得從新判斷。個人作法就是設置要給key
存到緩存中,而後在index.js
中也就是主的入口中經過hook
的useEffect
獲取緩存中的key
判斷是否爲自動仍是手動,若是是手動就獲取對應的value
經過Appearance.set
方法設置咱們的主題顏色。
這裏的Appearance
是經過react-native-appearance
導入的。
因爲是跨端的而後手機也是各式各樣這個頁面適配的js
是必不可少的,否則在不一樣的手機上就會產生頁面bug
顯示不一。主要是經過基準的設計稿做爲固定參數而後再經過api
獲取設備的屏幕寬高進行相除獲取對應值而後再乘設置的值。
還有就是橫屏的適配,其實就是判斷是否爲橫屏而後再經過將基準的寬度設置爲屏幕的高度,這樣橫過來也就能就進行頁面的適配了。
在react-native
開發中主要的仍是環境的搭建可能比較費時。在寫法中跟react
中基本都是同樣的,除了樣式的編寫,native
中主要用style
的形式用Object
的形式,寫多了感受跟寫css
就剝離了,以前寫多了react-native
的樣式,而後回去寫css
就會總會寫成native
中同樣寫Object
的形式。
還有就是如今的react-native
使用第三的包都不用手動的link
了,如今自動會關聯進去,因此使用仍是很方便的。雖然有時候使用的仍是有各類bug
因此儘可能不要去使用好久之前的第三方包。😂😂