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
因此尽可能不要去使用好久之前的第三方包。😂😂