最近公司有
移动端Android
的项目, 使用的技术栈是ReactNative
, 准备学习一下, 了解一下技术栈, 首先需要做的就是搭建开发环境, 可以自己选择的电脑系统和安卓/ios移动端
准备 Android 开发环境
node => v17.8.0
yarn => 1.22.17
javac => 11.0.14.1
Android Studio
Android 手机 => 打开开发者模式 => usb调试 (不使用模拟机)
创建第一个项目
# 不要单独使用常见的关键字作为项目名
# 不要在目录、文件名中使用中文、空格等特殊符号
npx react-native init firstapp
# --template 使用模板
npx react-native init AwesomeTSProject --template react-native-template-typescript
运行生成 firstapp 目录解析
.
├── App.js // index.js 引用了 App.js 显示内容
├── Gemfile // 这个是 ruby 的包管理, 类似 package.json
├── Gemfile.lock // 类似于前端的 yarn.lock
├── README.md
├── __tests__ // 可以写测试
├── android // 原生 Android 工程文件夹
├── app.json // 可以设置 app 的名字 配置项
├── babel.config.js // 配置 babel
├── index.js // RN移动端项目的入口
├── ios // 原生 ios 工程文件夹
├── metro.config.js // metro是一种支持ReactNative的打包工具配置
├── node_modules
├── package.json
└── yarn.lock
启动项目
- 查看我们的设备, 图片显示我们连接了一台
android
手机
- 启动项目
# 查看 package.json
yarn android
or
yarn react-native run-android
项目默认首页
- 我们可以看到上面项目启动以后有一个默认的首页,
分析代码
- 入口
index.js
// AppRegistry 是所有 React Native 应用的 JS 入口
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
// 根组件通过 AppRegistry.registerComponent 方法注册自己
// 原生系统才可以加载应用的代码包并且在启动完成之后通过调用AppRegistry.runApplication来真正运行应用
AppRegistry.registerComponent(appName, () => App);
-
index.js
引用了App.js
, 里面的内容比较多, 改成我们熟悉的Hello World
// 引用React react-native 原生组件
import React from 'react';
import {Text, View} from 'react-native';
const App = () => {
return (
<View>
<Text>Hello World!</Text>
</View>
);
};
export default App;
- 玩一下, 删除
App.js
修改入口为android_app/app.js
- 创建
android_app
文件夹 - 创建
android_app/app.js
- 修改
index.js
- 创建
import {AppRegistry} from 'react-native';
import App from './android_app/app'; // 修改引用
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
- 更新目录
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── __tests__
├── android
├── android_app // 新建的目录
│ └── app.js // 入口指向
├── app.json
├── babel.config.js
├── index.js
├── ios
├── metro.config.js
├── node_modules
├── package.json
└── yarn.lock
创建自己的文件夹
我们上面的代码创建了
android_app
文件目录, 这里就作为我们开发Android
项目的文件夹
- 可以创建
pages/
作为我们不同页面的目录 - 可以创建
components/
作为我们项目中组件目录 - 可以创建其它目录, 就像平常的前端开发一样
调试代码
运行代码如何调试呢?
-
首先我们启动项目的时候,会打开一个
metro.pngMetro
命令行工具
-
在手机上我们可以看到打开了一个
debug.jpgdevelopment menu
-
选中
Debug
会在我们的电脑上打开一个 浏览器 Tab 页, 打开开发者调试工具, 既可以调试了
对比学习法, 写一个 TodoList
todo 基本样式, 包含新增和删除对比就是: 用我们熟悉的前端
React
写法 来修改为ReactNative
写法, 下面我们在脑子里想象一下写一个我们熟悉的TodoList
, 你用React
怎么写, 如果换成RN
环境我们该怎么写
- 上面的图片就是我在
android
上写的一个todo
, 看一下代码对比一下和我们开发web
有什么不同- 下面的代码使用的编程语言也是
React hook
, 也是JSX
语言 - 不同点就是引用了
react-native
的一些原生组件, 没有使用div p span
等html
- 下面的
View 标签
可以当做div
看,Text 标签
当做span
,TextInput
当做input
, - 其实这些标签我们也可以当做我们引用了
react-native 组件库
在使用 - 还有一个比较大的不同
css 样式
, 这里使用的是对象写法
, 这个需要我们熟悉一下
- 下面的代码使用的编程语言也是
// 找到我们的 anndroid_app/app.js
import React, {useState} from 'react';
import {
Text,
View,
TextInput,
TouchableHighlight,
TouchableOpacity,
} from 'react-native';
import styles from './style.js';
const App = () => {
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const onPressAddTodo = () => {
setList([...list, input]);
};
const onPressDeleteItem = index => {
const copy = [...list];
copy.splice(index, 1);
setList(copy);
};
return (
<View style={styles.todo_wrap}>
<View style={styles.input_wrap}>
{/* 输入框 */}
<TextInput
style={styles.input}
onChangeText={text => setInput(text)}
value={input}
/>
{/* 按钮 */}
<TouchableHighlight onPress={onPressAddTodo}>
<View style={styles.button}>
<Text>新增todo</Text>
</View>
</TouchableHighlight>
</View>
{/* todo列表 */}
<View style={styles.list_wrap}>
{list.map((item, index) => {
return (
<View style={styles.item} key={index}>
<Text>{item}</Text>
<TouchableOpacity
style={styles.button}
onPress={() => onPressDeleteItem(index)}>
<Text>删除{index}</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</View>
);
};
export default App;
// android_app/styles.js
import {StyleSheet} from 'react-native';
const styles = StyleSheet.create({
todo_wrap: {
flex: 1,
borderWidth: 5,
borderStyle: 'solid',
borderColor: 'pink',
margin: 10,
},
input_wrap: {
marginTop: 20,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
},
button: {
marginTop: 20,
alignItems: 'center',
backgroundColor: 'dodgerblue',
padding: 10,
},
list_wrap: {
borderWidth: 3,
borderStyle: 'solid',
borderColor: 'red',
marginTop: 10,
},
item: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 10,
paddingBottom: 10,
fontSize: 16,
borderBottomWidth: 2,
borderBottomColor: '#ccc',
},
});
export default styles;
看了上面的代码, 我们基本可以对
React Native
有了一个基本的了解, 下面我们来看一下React Native
一些常用的组件和API
, 简单做个总结, 可以看官方文档
React-Native 常用组件
ReactNative 虽然使用的是
react
技术栈, 但JS
将 React 基础抽象组件渲染为原生平台UI组件, 有一些自己独有的元素
和api
基础组件
-
Text:
显示文本内容的组件, 支持文本和样式的嵌套以及触摸事件处理
<Text style={styles.baseText} onPress={onPress} selectable>
-
TextInput:
输入框组件, 支持自动拼写修复, 自动大小写切换, 展位默认字符以及多种键盘设置
<TextInput
onChangeText={(text) => onChangeText(text)}
value={value}
autoCorrect
{...restProps}
/>
-
Image:
图片展示组件, 支持多种类型图片的展示, 包括网络图片, 静态资源, 本地图片等
<Image
style={styles.tinyLogo}
source={{
uri: "https://reactnative.dev/img/tiny_logo.png",
}}
width={150}
height={150}
blurRadius={1}
/>
-
ActivityIndicator:
加载指示器组件(loading)
<ActivityIndicator animating={animating} />
-
Switch:
状态切换组件(开关)
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
onValueChange={toggleSwitch}
value={isEnabled}
/>
容器组件
-
View:
容器组件, 支持 Flexbox 布局, 样式, 触摸事件和处理一些无障碍功能, 可以放到其它容器组件, 也可以包含任意多个子组件
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Hello World!</Text>
</View>
</View>
-
ScrollView:
一个通用的滚动容器组件, 支持垂直和水平两个方向上的滚动, 必须有一个确定的高度才能正常工作 -
Touchable[Opacity | Highlight | WithoutFeedback | NativeFeedback]:
透明触摸/高亮触摸/无反馈/水波纹 => 用于封装视图,使其可以正确响应触摸操作
<TouchableOpacity onPress={press}>
<Text style={{ color: "red" }}>跳转到下一页面</Text>
</TouchableOpacity>
列表组件
-
VirtualizedList:
列表组件: 文档地址- 一般来说: 除非特殊的性能要求, 不建议直接使用, 因为 VirtualizedList 是一个抽象组件, 实际开发中, 使用 FlatList 和 SectionList 组件即可满足开发需求, 它们基于 VirtualizedList 组件扩展的
-
FlatList:
适用于加载长列表数据, 高性能的简单列表数据
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id} // key
// 分割线 => borderBottom 或者 ItemSeparatorComponent
// 下拉刷新/上拉加载更多
refreshing // 是否处于正在刷新的状态
onRefresh // 开始刷新事件, 发起接口请求
onEndReached // 上拉加载更多
onEndReachedThreshold={0} // 当距离内容最底部还有多远时触发onEndReached回调, 此参数是一个比值而非像素单位。比如,0.5 表示距离内容最底部的距离为当前列表可见长度的一半时触发
/>
-
SectionList:
高性能分组列表组件, 不同于 FlatList, SectionList 主要用于开发列表分组, 吸顶悬浮等功能
const DATA = [
{title: '', data: []}
]
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />} // 渲染每一个列表视图
renderSectionHeader={({ section: { title } }) => ( // 渲染每一个 section中的每一个列表项视图
<Text style={styles.header}>{title}</Text>
)}
/>
平台组件
适用于 Android 和 Ios 自己平台的组件, 查看文档
ReactNative 常用 API
组件是构成页面视图的基本元素, API 就是构成功能模块的基本元素
基础 API
-
AppRegistry
: AppRegistry 是所有 React Native 应用的 JS 入口。应用的根组件应当通过 AppRegistry.registerComponent 方法注册自己
AppRegistry.registerComponent(appName, () => App);
-
AppState:
告诉你应用当前是在前台还是在后台,并且能在状态变化的时候通知你
active: 应用正在前台运行;
background: (在后台运行) => 在别的应用;
停留在桌面;
inactive: 正在前后台的切换过程中;
useEffect(() => {
AppState.addEventListener("change", _handleAppStateChange);
return () => {
AppState.removeEventListener("change", _handleAppStateChange);
};
}, []);
AppState.currentState;
-
NetInfo:
用于获取手机联网状态的 API
// none: 离线;
// wifi: wifi联网;
// cellular: 通过蜂窝数据联网
// unkonwn: 联网状态异常
NetInfo.getConnectionInfo().then(); // 获取手机联网状态
NetInfo.addEventListener(name, handler); // 监听网络状态
NetInfo.isConnectionExpensive(); // 连接是否收费
-
AsyncStorage:
异步 持久化数据存储 API, 它以键值对方式保存数据 => 全局的, 先封装后再使用
// 常用的:
getItem(): 根据键值获取数据, 结果返回回调函数
setItem(): 保存值
removeItem(): 删除
mergeItem(): 合并已有的值和新的值
clear(): 清除
-
DeviceEventEmitter:
发布订阅模式, 在两个相互独立的组件之间通信使用, 类似 Vue 的EventBus
DeviceEventEmitter.addListener("我要吃饭", (草莓) => {
console.log(`我要吃${草莓}`);
});
DeviceEventEmitter.remove();
DeviceEventEmitter.emit("我要吃饭", "草莓");
屏幕相关 API
-
Dimensions:
用于获取设备屏幕的宽高。
Dimensions.get("window");
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;
-
PixelRatio:
取到设备的像素密度和字体缩放比, 设备像素: 物理像素/设备独立像素- 例如: ip4 => 屏幕物理像素 640, 独立像素 320, PixelRatio 为 2
- RN 开发使用的尺寸单位是 pt, 由于移动设备像素密度不一样, 1pt 对应的像素也不一样
- 屏幕分辨率 = 屏幕宽高 * 屏幕像素密度
get() => 1: mdpi android devices; 1.5: hdpi android devices; ... // 设备的像素密度
getFontScale() // 字体大小缩放比例
动画 API
-
requestAmimationFrame:
帧动画, 通过不断改变组件的状态来实现动画效果 -
LayoutAnimation:
布局动画, 当布局发生改变时的动画模块 布局动画文档 -
Animated:
侧重于输入和输出之间的声明性关系,以及两者之间的可配置变换,此外还提供了简单的 start/stop 方法来控制基于时间的动画执行
平台 API
-
BackHandler:
用于监听 Android 设备返回事件的 API
// 示例: android 退出应用
BackHandler.addEventListener("hardwareBackPress", () => {
console.log("再按一次退出应用");
BackHandler.exitApp();
});
BackHandler.removeEventListener("hardwareBackPress", () => {});
-
PermissionsAndroid:
访问 Android M(也就是 6.0)开始提供的权限模型, 仅对 Android 平台有效
CAMERA: (相机权限) => android.permission.CAMERA;
READ_CALENDAR: (日历) => android.permission.READ_CALENDAR;
...等等
check() // 检测用户是否授权过某个动态权限
request() // 弹出提示框向用户请求某项动态权限
requestMultiple() // 多个权限
第三方库
- NativeBase: 一款优秀的 RN 组件库,提供了丰富的第三方组件
yarn add native-base
- react-native-elements: 也是一个常见的 ReactNative 组件库
yarn add @rneui/themed @rneui/base
yarn add react-native-vector-icons
// 支持三种类型的导航器
StackNavigator: 包含导航栏的页面导航组件
TabNavigator: 底部展示 TabBar 的页面导航组件
DrawerNavigator: 实现侧边栏抽屉页面的导航组件
- react-native-snap-carousel: 一个轮播组件库
<Carousel
data={this.state.entries} // 数据源
renderItem={this._renderItem} // 渲染单个视图
sliderWidth={sliderWidth} // 循环容器的宽度
itemWidth={itemWidth} // 子元素的宽度
/>
网友评论