React Native 学习笔记

引言

什么是 RN

RN 是 React Native 的简称, 它允许我们通过 react 的语法, 开发移动端 APP.

可以前往如下网址查看详细内容, 在这里仅作笔记记录, 可能不是很全.

APP 开发模式

移动端 APP 的开发模式有原生开发, 以及混合开发. 原生就是类似于 Android 就使用 Java / Kotlin, IPhone 就使用 Swift. 混合开发则指的是一次开发, 多端部署, RN 就是其中的一个. 另外的就是 H 5 开发, 其实就是普通的网页应用.

RN 就是本文所讲内容, 还有 Weex 以及 Flutter, 这里做一个对比.

混合开发框架比较

框架 RN Weex Flutter
公司 Facebook Alibaba Google
语言 JS (React) JS (Vue) Dart
引擎 JSCore V 8 Flutter Engine
系统 Android, IOS Android, IOS, Web Android, IOS, Fuchsia
性能 一般 较快 较快
场景 整体 APP 单页面 整体 APP
学习成本 一般

RN 的优势

  1. 开发体验好. 用统一的代码规范开发移动端程序, 不需要关注移动端的差异.
  2. 开发成本低. 一次开发多端部署.
  3. 学习成本低. 只需要掌握 JS 和 react, 就可以开始移动端开发了.

RN 的不足

  1. 项目版本更新维护较为频繁, 学习成本高.
  2. 试错成本高, 有些问题没有解决方案.
  3. 性能不如原生.
  4. 涉及底层功能的时候, 还是需要针对 Android 和 iOS 进行针对开发.

React Native 基础环境搭建

基础环境

首先, 需要安装 NodeJS (>=12), 这里不过多介绍. 随后, 我们需要安装一下 React Native 的脚手架:

[!bug] 注意
不建议全局安装脚手架, 建议直接跳过本部分.

1
2
3
4
5
# 全局安装脚手架
npm install -g react-native-cli

# 卸载全局脚手架
npm uninstall -g react-native-cli @react-native-community/cli

安卓环境

如果使用的是 Windows, 那么只能搭建安卓开发环境, 不能搭建 iOS 的开发环境, 因为 Swift 编译器只能在 iOS 桌面端使用.

为了搭建安卓的开发环境, 首先我们需要安装 JDK, 也就是需要一个 Java 环境. 这里使用的 JDK 版本请查阅当前的文档内容, 我的文档内容如下:

其次, 安装一下 Android Studio, 直接下载就好:

下载还好, 不需要翻墙; 启动的时候会安装一些对应的组件, 这里需要 翻墙, 否则可能会出现无法正常安装的情况.

[!warning] 注意
安装时间可能非常久, 一定要有耐心.

随后, 需要安装一下 Android SDK, 这里还是查阅一下官方文档的说明再进行安装:

然后来到 Studio, 选择对应的版本进行安装.

SDK Tool 应该会自动安装结束, 这里不做介绍.

另外, 还需要配置一下安卓 SDK 的安装环境变量, 创建一个叫做 ANDROID_HOME 的环境变量即可.

|450

此外, 还需要自己创建一个可运行的安卓虚拟机, 否则可能会出现未知报错.

至此, 基本就安装完毕了, 环境变量根据官网配置一下就好.

初始化项目

创建项目

可以使用脚手架工具, 也可以直接使用如下命令:

1
2
3
4
5
# 脚手架创建
react-native init myProject

# 不需要脚手架 命令创建
npx @react-native-community/cli init AwesomeProject

可以看到项目结构如下:

创建项目了, 直接进入目录, 使用如下命令就可以运行了: (记得安装 yarn)

1
yarn android

解决报错

如果遇到报错:

直接点我下载安装包, 并且解压到如下路径即可: (如果是别的版本, 下载对应的版本放在对应的文件夹中一样).

1
C:\Users\<用户名>\.gradle\wrapper\dists\gradle-4.7-all\4cret0dgl5o3b21weaoncl7ys

如果遇到没有 NDK 的报错, 请安装如下内容 (NDK 也可以根据报错信息选择正确的版本)

随后, 在项目的 android 目录下, 创建一个 local.properties 文件, 在里面指定一下 ndk 的路径:

1
2
# 这里注意 Windows需要两个反斜杠
ndk.dir=C:\\DevEnvs\\Android\\Sdk\\ndk\\27.1.12297006

如果遇到下载慢的问题, 可以考虑使用魔法或者直接在网上下载对应内容后覆盖文件夹.


随后再次执行该命令即可, 等待一会很久, 项目就跑起来了:

这里还要注意, 模拟器一定要开机完成才能够正常运行, 否则会报错.

调试工具

模拟器就是自动启动的一个东西, 是跟随 Android Studio 进行安装的, 如果没有安装, 再次进行安装即可; 真机调试需要使用 USB 调试.

一般的, 我们使用模拟器进行调试即可.

基础语法

React 语法

其实就是下面这几个部分:

  1. JSX 语法
  2. 组件 (分类, 传参, 属性, 状态)
  3. 生命周期
  4. Hook API
  5. Redux
  6. 常用的包

如有需要, 可以查看这篇文章: React 学习笔记 | FuMOE, 我在这里仅记录新的东西.

StyleSheet

这是 RN 中用来声明样式的 API. RN 的样式和 CSS 有所不同:

  1. 没有继承性: 即子元素不会被父元素的样式影响 (Text 组件除外)
  2. 样式名称采用小驼峰命名 (fontSize)
  3. 所有尺寸是没有单位的 (width: 100)
  4. 有一些特殊的样式名称 (marginHorizontal)

在 RN 中, 可以按照 React 的方式进行样式声明, 也可以按照数组+多个样式对象的方式, 如果属性重叠, 则后面的会覆盖前面的.

创建组件

查看 index.js, 可以看到如下代码:

1
2
3
4
5
6
7
8
9
10
11
/**
* @format
*/

// 引入一个东西 帮我们注册组件
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';

// 注册组件 第一个是组件名称 查看发现就是项目名称, 第二个是一个回调函数, 返回一个React组件
AppRegistry.registerComponent(appName, () => App);

另外, 我们不要原来的 App, 直接新建一个出来.

1
2
3
4
5
6
7
8
9
10
11
12
import { FC } from 'react';
import { Text, View } from 'react-native';

const App: FC = () => {
return (
<View>
<Text>Hello World</Text>
</View>
);
};

export default App;

查看发现, 左上角有一个 Hello World 了!

|225

我们也可以直接创建一个 src/components 目录, 写上想要的组件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// HelloWorld.tsx
import { FC, useState } from 'react';
import { Text, View, Button } from 'react-native';

const HelloWorld: FC = () => {
const [counter, setCounter] = useState(0);
return (
<View>
<Text>Hello World</Text>
<Button title="-1" onPress={() => setCounter(counter - 1)} />
<Text>{counter}</Text>
<Button title="+1" onPress={() => setCounter(counter + 1)} />
</View>
);
};

export default HelloWorld;

随后正常使用该组件即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// App.tsx
import { FC } from 'react';
import { View } from 'react-native';
import HelloWorld from './src/components/HelloWorld';

const App: FC = () => {
return (
<View>
{/* 正常调用组件 */}
<HelloWorld />
</View>
);
};

export default App;

效果是没有问题的.

|325

行内样式

接下来, 就可以尝试给组件添加样式了, 比如给 text 添加属性.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// HelloWorld.tsx
import { FC } from 'react';
import { Text, View } from 'react-native';

const HelloWorld: FC = () => {
return (
<View>
{/* 注意 RN中是没有单位的 */}
<Text style={{ fontSize: 30 }}>Hello World</Text>
</View>
);
};

export default HelloWorld;

|330

数组的行内样式

也可以用数组的方式, 后出现的会覆盖前面出现的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { FC } from 'react';
import { Text, View } from 'react-native';

const HelloWorld: FC = () => {
return (
<View>
{/* 注意 RN中是没有单位的 */}
<Text style={{ fontSize: 30 }}>Hello World</Text>
<Text
style={[
{ color: 'orange' },
{
fontSize: 25,
},
// 如果颜色重复 则会覆盖原来的
{ color: 'green' },
]}
>
YYt and Lc
</Text>
</View>
);
};

export default HelloWorld;

|330

StyleSheet 样式

另外, 就是使用 StyleSheet 声明样式了, 自然需要引入后再使用. 这里需要使用一个方法, 返回的就是可以直接使用的样式了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { FC } from 'react';
import { Text, View, StyleSheet } from 'react-native';

// 定义样式
const styles = StyleSheet.create({
// 可以写任意的类 比如title
title: {
fontSize: 30,
fontWeight: 'bold',
},
});

const HelloWorld: FC = () => {
return (
<View>
<Text style={styles.title}>Hello World</Text>
</View>
);
};

export default HelloWorld;

感觉好一些, 避免了行内样式的问题.

Flexbox

手机端的弹性布局和普通的不太一样. 这里有一些 Flexbox 的术语:

  • 容器 (container)
    • 采用 Flex 布局的元素, 称为 Flex 容器
  • 项目 (item)
    • 容器的所有子元素, 称为 Flex 项目
  • 主轴 (main axis)
  • 交叉轴 (cross axis)

对应的, 我们可以设置一个 Flex 容器的主轴方向, Web 默认是 row, RN 默认则是 column. 另外还可以设置主轴, 交叉轴的对其方式, 以及一些比例, 基础代码如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { FC } from 'react';
import { Text, View, StyleSheet } from 'react-native';

const style = StyleSheet.create({
mainContainer: {
margin: 20,
},
title: {
fontSize: 30,
fontWeight: 'bold',
},
subTitle: {
fontSize: 25,
fontWeight: 'bold',
color: '#333',
},

// 列表容器
namesContainer: {
height: 150,
margin: 10,
borderWidth: 1,
borderColor: '#ddd',
},

// 名称项
nameItem: {
backgroundColor: 'lightgreen',
// 设置文字居中
textAlign: 'center',
borderColor: '#000',
borderWidth: 1,
margin: 5,
},
});

const FlexTest: FC = () => {
return (
<View style={style.mainContainer}>
<Text style={style.title}>Flex布局测试</Text>
<Text style={style.subTitle}>主轴方向</Text>
<View style={style.namesContainer}>
<Text style={style.nameItem}>小猫</Text>
<Text style={style.nameItem}>YYT</Text>
<Text style={style.nameItem}>LC</Text>
</View>
</View>
);
};

export default FlexTest;

显示基础效果如下:

|330

接下来, 可以设置一下方向:

1
2
3
4
5
6
7
8
// 列表容器
namesContainer: {
height: 150,
margin: 10,
borderWidth: 1,
borderColor: '#ddd',
flexDirection: 'row',
},

|330

也可以根据需要进行修改, 其实这个就是普通的 flex 布局的包装, 剩下的还是不介绍了.

响应式布局

简单说, 就是让元素根据屏幕的大小进行自适应, 而不是直接写死一个大小. 这里需要用到一个新的东西: Dimensions, 它可以帮助我们获取屏幕的大小以及窗口的大小.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { FC } from 'react';
import { Text, View, StyleSheet, Dimensions } from 'react-native';

const styles = StyleSheet.create({
container: {
// 水平排列
flexDirection: 'row',
paddingTop: 20,
},
h1: {
fontSize: 35,
fontWeight: 'bold',
},
itemBase: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00b38a',
// 宽度 使用响应式的思路
// 我希望宽度是占屏幕的1/4 就可以使用下面这个方法
width: Dimensions.get('window').width / 4,
height: 80,

// 如果要添加边框 这里的border不支持符合的写法
borderWidth: 1,
borderColor: 'yellow',
// 由于样式没有继承性 所以需要单独声明样式
},
itemText: {
fontSize: 20,
color: 'white',
fontWeight: 'bold',
},
});

const TestComponent: FC = () => {
return (
<View style={styles.container}>
{/* 模拟一个响应式布局 */}
<View style={styles.itemBase}>
<Text style={styles.itemText}>扫一扫</Text>
</View>
<View style={styles.itemBase}>
<Text style={styles.itemText}>付款码</Text>
</View>
<View style={styles.itemBase}>
<Text style={styles.itemText}>卡包</Text>
</View>
<View style={styles.itemBase}>
<Text style={styles.itemText}>出行</Text>
</View>
</View>
);
};

export default TestComponent;

这一行的东西就出现了.

|330

另外, 如果我希望一行是 3 个, 直接修改为 / 3 的话, 会发现最后面的出行没了.

|330

这里需要设置一下换行, flexWrap 属性.

1
2
3
4
5
6
container: {
// 水平排列
flexDirection: 'row',
flexWrap: 'wrap',
paddingTop: 20,
},

这样显示就正常了.

|330

核心组件

RN 中的核心组件其实就是对原生组件的一些封装, 原生组件就是 Android 或者 IOS 内部就写好的一些直接使用的组件.

其实就是一些非常常用的组件, 还是有必要了解一下的, 不了解也没关系, 直接看文档, 根据需要走就行.

Button

顾名思义, 就是按钮. 直接查看代码及效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { FC } from 'react';
import { Text, View, StyleSheet, Button, Alert } from 'react-native';

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
gap: 10,
},
h1: {
fontSize: 30,
fontWeight: 'bold',
},
});

const TestComponent: FC = () => {
return (
<View style={styles.container}>
<Text style={styles.h1}>按钮组件</Text>
{/* 对于一个按钮来说 需要使用单标签 */}
<Button
title="Alert"
onPress={() => {
Alert.alert('按钮点击了');
}}
/>

{/* 可以设置颜色 */}
<Button
title="Alert"
onPress={() => {
Alert.alert('按钮2点击了');
}}
// 可以配置颜色
color={'red'}
/>
</View>
);
};

export default TestComponent;

|330

Alert

其实就是一个对话框. 使用的时候配合按钮一起使用即可. 按钮的点击事件是 onPress:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import { FC } from 'react';
import { Text, View, StyleSheet, Button, Alert } from 'react-native';

const style = StyleSheet.create({
mainContainer: {
margin: 10,
gap: 10,
},
title: {
fontSize: 35,
fontWeight: 'bold',
},
});

const FlexTest: FC = () => {
return (
<View style={style.mainContainer}>
<Text style={style.title}>Alert测试</Text>
{/* 使用的时候 使用的是 title来指定 */}
<Button
title="我是一个按钮"
onPress={() => {
// 通过Alert来触发弹窗
Alert.alert('我是一个弹窗');
}}
/>
<Button
title="多个按钮的弹窗"
onPress={() => {
// 通过Alert来触发弹窗
Alert.alert('我是标题', '我是消息内容', [
{
text: '按钮1',
onPress: () => console.log('点击按钮1'),
},
{
text: '按钮2',
onPress: () => console.log('点击按钮2'),
},
]);
}}
/>

<Button
title="选项文本可以自定义样式"
onPress={() => {
Alert.alert('我是标题', '我是消息内容', [
{
text: '按钮1',
style: 'destructive',
onPress: () => console.log('点击按钮1'),
},
{
text: '按钮2',
style: 'cancel',
onPress: () => console.log('点击按钮2'),
},
]);
}}
/>
</View>
);
};

export default FlexTest;

这就是基础的点击事件了.

|330

StatusBar

这个就是手机最上面的, 显示电量之类内容的地方. 可以通过一个布尔值开控制开关.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { FC } from 'react';
import { Text, View, StyleSheet, StatusBar } from 'react-native';

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
gap: 10,
},
h1: {
fontSize: 30,
fontWeight: 'bold',
},
});

const TestComponent: FC = () => {
return (
<View style={styles.container}>
<StatusBar
// 是否显示
hidden={false}
backgroundColor={'red'}
// 设置图标的颜色
barStyle={'dark-content'}
/>
<Text style={styles.h1}>设置状态栏</Text>
</View>
);
};

export default TestComponent;

然后就可以看到上面的这个 Bar 了.

|330

Switch

就是一个简单的开关按钮, 可以配合上面的 StatusBar 来进行练习.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { FC, useState } from 'react';
import { Text, View, StyleSheet, StatusBar, Switch } from 'react-native';

const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
padding: 20,
gap: 10,
},
h1: {
fontSize: 30,
fontWeight: 'bold',
},
});

const TestComponent: FC = () => {
const [showStatusBar, setShowStatusBar] = useState(true);
return (
<View style={styles.container}>
<Text style={styles.h1}>设置状态栏</Text>
<StatusBar
// 是否显示
hidden={showStatusBar}
backgroundColor={'red'}
// 设置图标的颜色
barStyle={'dark-content'}
/>
<Switch
// 设置颜色
trackColor={{ false: 'red', true: 'green' }}
// 小圆点的颜色
thumbColor={'blue'}
// 设置值
value={showStatusBar}
// 实现切换
onValueChange={setShowStatusBar}
/>
</View>
);
};

export default TestComponent;

|330

矢量图标库

有的时候我们可能会遇到需要使用图标按钮之类的情况, 这个时候我们就可以直接使用这个第三方组件库了.

直接使用如下命令进行安装: (这里查阅文档, 根据需要安装需要的即可)

1
npm install @react-native-vector-icons/fontawesome6 @react-native-vector-icons/evil-icons

对于安卓来说, 最新版已经不需要链接图标库之类的操作了, 直接重新编译程序即可.

路由导航

基础安装

一个程序自然有很多的页面, 我的, 设置这种, 一堆一堆. 那么我们自然就需要使用路由了. 在 RN 中, 我们使用的是 React Navigation 这个路由, 不是网页端的 React Router, 这俩是不一样的.

[!important] 注意
这里使用的路由版本为 5.x, 不是最新版.

我们使用的不是 expo, 所以可以忽略一些东西. 下面这些是必须要进行安装的:

1
2
3
4
5
# 安装核心
npm install @react-navigation/native@^5.x

# 安装依赖
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

随后, 需要进行配置, 来到程序的顶部, 也就是 App.tsx 或者 index.js, 可以进行注册.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Text, View } from "react-native";

// 路由相关
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";

function App() {
return (
// 包裹一下整体
<NavigationContainer>
<View>
<Text>Hello World</Text>
</View>
</NavigationContainer>
);
}

export default App;

至此, 基本的配置就算结束了.

Stack 导航

RN 中没有类似浏览器的 History 对象, 所以我们不能通过这个东西进行跳转. 在 RN 中跳转之前, 需要先将路由声明在 Stack 中, 才能进行跳转.

安装

Stack 导航也是要单独进行安装的, 否则不能正常使用.

1
npm install @react-navigation/stack@^5.x

基础使用

参考如下代码进行使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { StyleSheet, Text, View } from "react-native";

// 路由相关
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";

// 引入Stack路由
import { createStackNavigator } from "@react-navigation/stack";

// 实例化stack路由
const Stack = createStackNavigator();

// 提供两个组件 作为路由的东西
const HomeScreen = (props) => {
return (
<View style={styles.container}>
<Text style={[styles.text]}>This is Home Screen</Text>
</View>
);
};

const DetailScreen = (props) => {
return (
<View style={styles.container}>
<Text style={[styles.text]}>This is Detail Screen</Text>
</View>
);
};

function App() {
return (
// 包裹一下整体
<NavigationContainer>
{/* 声明导航的详细内容 */}
<Stack.Navigator>
{/* 这就是一个容器 包含了多个路由 */}
{/* screen就是单个项目 需要name, 路由名称以及component, 路由显示的内容 */}
{/* 默认加载的是第一个 Home */}
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 30,
},
});

export default App;

可以看到显示的效果还是很不错的.

|220

但是目前没法跳转, 我们实现以下跳转的效果.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 提供两个组件 作为路由的东西
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={[styles.text]}>This is Home Screen</Text>
<Button
title="前往详情页"
onPress={() => {
// 这里使用props里面的一个东西进行跳转
// 通过名称进行跳转
navigation.navigate("Detail");
}}
/>
</View>
);
};

const DetailScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={[styles.text]}>This is Detail Screen</Text>
<Button
title="跳转到主页"
onPress={() => {
navigation.navigate("Home");
}}
/>
</View>
);
};

现在, 点击就可以触发跳转效果了.

|220

常用属性

默认页面

现在默认加载的是 Home 页面, 如果我希望默认加载别的页面, 可以配置一个属性:

1
2
3
4
5
6
7
8
9
10
11
12
function App() {
return (
// 包裹一下整体
<NavigationContainer>
{/* 配置初识路由名称 */}
<Stack.Navigator initialRouteName="Detail">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

现在默认的页面就变了:

|220

顶栏隐藏

默认的我们顶部有一个 Header, 我们可能想要去除这个东西, 就可以使用 headerMode 属性.

1
2
3
4
5
6
7
8
9
10
11
function App() {
return (
<NavigationContainer>
{/* none 就是没了 */}
<Stack.Navigator headerMode="none">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

|220

这样页面就清爽多了.

顶栏配置

自然, 顶栏的文本之类的东西都是可以配置的, 比如配置标题, 样式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<NavigationContainer>
{/* none 就是没了 */}
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
// 标题
title: "首页",
// 样式
headerStyle: {
backgroundColor: "lightblue",
},
// 也可以配置右边有一个按钮
headerRight: () => (
<TouchableOpacity
onPress={() => {
Alert.alert("Hello");
}}
>
<Text>Hello</Text>
</TouchableOpacity>
),
}}
/>
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>

现在就可以看到顶栏的变化了.

|330

BottomTab 导航

其实就是下面的底部导航栏啦, 使用的规范还是符合第三方组件的使用规范的.

安装

直接在项目中执行如下命令即可:

1
npm install @react-navigation/bottom-tabs@^5.x

基础使用

和 Stack 的一样, 引入后进行实例化, 然后配置即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 路由相关
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";

// 引入并实例化
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const BottomTabNavigator = createBottomTabNavigator();

// 引入别的页面
import HomeScreen from "./src/views/HomeScreen";
import DetailScreen from "./src/views/DetailScreen";

function App() {
return (
<NavigationContainer>
<BottomTabNavigator.Navigator>
<BottomTabNavigator.Screen name="Home" component={HomeScreen} />
<BottomTabNavigator.Screen name="Detail" component={DetailScreen} />
</BottomTabNavigator.Navigator>
</NavigationContainer>
);
}

export default App;

可以看到, 下面已经出现一个底部导航栏了, 并且点击可以切换到不同的页面.

|220

常用属性

图标

作为底部导航栏, 肯定是需要一个图标的. 所以我们需要进行一个配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function App() {
return (
<NavigationContainer>
{/* 在总体部分配置图标 */}
<BottomTabNavigator.Navigator
screenOptions={(route) => ({
// focused 当前菜单是否活跃
// color 颜色
// size 大小
// eslint-disable-next-line react/no-unstable-nested-components
tabBarIcon: ({ color }) => {
let iconName;
if (route.route.name === "Home") {
iconName = "主页";
} else {
iconName = "详情";
}
return <Button title={iconName} color={color} />;
},
})}
>
<BottomTabNavigator.Screen name="Home" component={HomeScreen} />
<BottomTabNavigator.Screen name="Detail" component={DetailScreen} />
</BottomTabNavigator.Navigator>
</NavigationContainer>
);
}

这里暂时用一个按钮替代图标, 使用一个配置函数来实现效果.

|330


然而, 这样太丑了, 我们需要的肯定是一个小图标那种样子. 这里可以使用之前配置好的矢量图标库. 随后直接使用即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 路由相关
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";

// 引入并实例化
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const BottomTabNavigator = createBottomTabNavigator();

// 引入别的页面
import HomeScreen from "./src/views/HomeScreen";
import DetailScreen from "./src/views/DetailScreen";

// 引入图标组件库
import Icon from "@react-native-vector-icons/ionicons";

function App() {
return (
<NavigationContainer>
{/* 在总体部分配置图标 */}
<BottomTabNavigator.Navigator
screenOptions={(route) => ({
tabBarIcon: ({ focused, color, size }) => {
return <Icon name="key" size={size} color={color} />;
},
})}
>
<BottomTabNavigator.Screen name="Home" component={HomeScreen} />
<BottomTabNavigator.Screen name="Detail" component={DetailScreen} />
</BottomTabNavigator.Navigator>
</NavigationContainer>
);
}

export default App;

另外需要在 android/app.build.gradle 的文件末尾加上这么一句:

1
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

然后效果就有啦~ 但是这里配置可能会出现问题, 直接查看官方文档即可.

|330

颜色

另外一个配置项, 可以单独配置颜色.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function App() {
return (
<NavigationContainer>
{/* 在总体部分配置图标 */}
<BottomTabNavigator.Navigator
// 配置颜色 在另外一个配置项
tabBarOptions={{
activeTintColor: "blue",
inactiveTintColor: "red",
}}
>
<BottomTabNavigator.Screen name="Home" component={HomeScreen} />
<BottomTabNavigator.Screen name="Detail" component={DetailScreen} />
</BottomTabNavigator.Navigator>
</NavigationContainer>
);
}

现在就可以看到颜色了:

|330

Drawer 导航

就是侧边的抽屉导航菜单, 符合第三方组件的使用方式.