一、写在前面
1. 前言
经过将近一个月的学习和开发,也做出了第一个由RN开发的 Android
应用。该文章旨在将近期学习的 React Native 所踩的坑、技术方案进行总结,为之后的开发留下可参考的财富,减少重复造轮子、踩坑的时间。(你所深陷的困境,可能别人早已轻而易举地解决,不要把脑力花在这上面!除非你想自己造轮子。)
2. 总结路线
- 环境、框架搭建相关
- 基础组件
- 第三方组件
- 样式
- 代码实现
- 常见报错
3. 输出内容
- 环境搭建:傻瓜式可操作步骤(参考「菜谱」)
- 组件:最简单用法 -> 效果 -> 扩展用法(参考「UI库官方文档」)
- 样式:希望实现的效果 -> 写法
- 代码实现:希望实现的功能 -> 写法 -> 注意点
- 报错:报错内容 -> 报错原因 -> 解决办法
二、环境、框架搭建
1. 在电脑上安装多个JDK版本
要让 RN 跑起来,JDK 环境是必须的,网上已有非常多的 JDK 安装教程,找一篇跟着操作即可
这里要解决的问题是:假设我电脑上已原有 JDK8 版本,而官方文档要求如果 RN 版本 >= 0.67
,则需要 JDK11
的版本。那么此时我该如何 **同时配置两个JDK环境?**操作步骤如下:
- 先正常安装 JDK8 和 JDK11 两个版本
- 安装完成后,按照下图步骤,配置两个环境变量,分别是
JAVA8_HOME
和JAVA11_HOME
- 若在当前电脑需要切换到
JDK8
的环境,则在Path
里配置如下(JDK11同理),并点击确定
- 在
Path
里将要使用的JDK版本放在最前面,即可完成切换
- 最后需打开 新 的cmd窗口,输入
java -version
验证当前版本是否已切换成功
如下图,即代表当前环境为 JDK11
2. 真机进行网络调试
方式一:react-native-debugger
方式二:reactotron
【推荐】
Reactotron 官方链接:https://github.com/infinitered/reactotron/blob/master/docs/quick-start-react-native.md
其使用非常简单,根据官方文档进行操作即可,大体分为两个步骤:
- 安装本地客户端
- 在项目代码中配置后,重新编译即可生效,并在客户端中看到效果如图
三、基础组件
1. TouchableOpacity
有点击样式的 块元素
可以理解为:加了
hover
样式的div
。与之不同的是,在RN中,<View>
无法添加onPress
来触发点击事件,因此需要在<View>
外层套一个<TouchableOpacity onPress={...}>
import { TouchableOpacity, Text } from 'react-native'
const handlePress = () => { ... }
const Index = () => {
<TouchableOpacity onPress={handlePress} activeOpacity={0.5}>
<View></View>
</TouchableOpacity>
}
常用属性:
activeOpacity
:控制手指按下时,元素的透明度变化值(类似css的 div:hover { opacity: 0.5 })
四、样式
1. 响应式
UI给出的设计稿的屏幕宽度通常都是
375
,也就是以iPhone6
作为标准来设计,而实际开发中,为了适配各种机型的显示,我们需要声明如下函数,并在样式中使用它
- 声明
// src/utils/stylesKits.js
import {Dimensions} from 'react-native';
// 手机中元素的宽度 = 手机屏幕 * 元素宽度 / 设计稿宽度(以375为例)
export const screenWidth = Dimensions.get('window').width;
export const screenHeight = Dimensions.get('window').height;
export const pxToDp = elePx => (screenWidth * elePx) / 375;
- 使用
// xxx.js 组件中编写styles时引用
import {pxToDp} from '../../utils/stylesKits';
const styles = StyleSheet.create({
image: {
width: pxToDp(150),
height: pxToDp(150),
}
});
2. 沉浸式导航栏
使用的路由和导航栏组件是
@react-navigation/native-stack
参考文档:https://reactnavigation.org/docs/native-stack-navigator
当希望实现类似「沉浸式导航栏」时,我们需要把顶部的导航栏隐藏,实现如下:
方法一:【不推荐,会占位】将导航栏的背景色和字体色调整成和页面背景色一致即可
<Stack.Navigator initialRouteName="Login">
<Stack.Screen
name="Login"
component={Login}
options={{
headerStyle: {
backgroundColor: '#fff',
},
// 隐藏标头上的高程阴影 (Android) 或底部边框 (iOS)。
headerShadowVisible: false,
headerTintColor: '#fff',
}}
/>
</Stack.Navigator>
方法二:【推荐】将 header
设置成 null
header
实际用途是:自定义标题栏,此时返回 null 相当于自定义了一个没有任何结构的导航栏,达到了隐藏原生导航栏的目的
<Stack.Navigator initialRouteName="Login">
<Stack.Screen
name="Login"
component={Login}
options={{
header: () => {
return null
},
}}
/>
</Stack.Navigator>
3. 设置渐变色
RN 无法像 CSS 一样,直接通过
linear-gradient
就可以设置渐变色需要引入第三方插件 https://www.npmjs.com/package/react-native-linear-gradient
- 安装
yarn add react-native-linear-gradient
- 简单使用
<LinearGradinet
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
colors={['#9b63cd', '#e0708c']}
style={{width: 200, height: 200}}
/>
五、写法
1. useEffect 中使用异步函数
当我尝试中 useEffect
中用如下代码写「异步函数」时,报错如下
useEffect must not return anything besides a function, which is used for clean-up.
// 错误写法
useEffect(async () => {
...
}, []);
正确写法如下
// 正确写法
useEffect(() => {
(async () => {
...
})();
}, []);
2. 缓存
原生自带的
AsyncStorage
已被官方废弃,其推荐使用社区的包
因此,我们使用 @react-native-async-storage/async-storage
(社区的其他包也可以,该包较为容易上手),地址:https://react-native-async-storage.github.io/async-storage/docs/install。简单使用如下:
- 安装
yarn add @react-native-async-storage/async-storage
- 在需要使用的地方导入
import AsyncStorage from '@react-native-async-storage/async-storage';
- 写入缓存
① 写入的是如「字符串
」之类的值类型
const storeData = async (value) => {
try {
await AsyncStorage.setItem('@storage_Key', value)
} catch (e) {
// saving error
}
}
② 写入的是如「对象、数组
」之类的引用类型(需要先将对象转换成字符串)
const storeData = async (value) => {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem('@storage_Key', jsonValue)
} catch (e) {
// saving error
}
}
- 读取缓存
① 读取「字符串
」之类的值类型
const getData = async () => {
try {
const value = await AsyncStorage.getItem('@storage_Key')
if(value !== null) {
// value previously stored
}
} catch(e) {
// error reading value
}
}
② 读取「对象、数组
」之类的引用类型
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('@storage_Key')
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch(e) {
// error reading value
}
}