React Native快速指南
本文将从 React Native 的基础用法出发,介绍如何使用一套代码构建 iOS 与 Android 应用。内容涵盖项目创建、基础组件、页面布局、导航配置、状态管理、接口请求、调试方式以及原生能力调用,帮助前端开发者快速理解 React Native 的开发流程,并掌握移动端跨平台应用的基本实践。
核心组件
| React Native UI 组件 | Android 视图 | iOS视图 | 网络模拟 | 描述 |
|---|---|---|---|---|
<View> |
<ViewGroup> |
<UIView> |
无需滚动<div> |
一个支持 Flexbox 布局、样式、部分触摸处理和辅助功能控制的容器 |
<Text> |
<TextView> |
<UITextView> |
<p> |
显示、设置样式和嵌套文本字符串,甚至可以处理触摸事件 |
<Image> |
<ImageView> |
<UIImageView> |
<img> |
显示不同类型的图像 |
<ScrollView> |
<ScrollView> |
<UIScrollView> |
<div> |
一个通用的滚动容器,可以包含多个组件和视图 |
<TextInput> |
<EditText> |
<UITextField> |
<input type="text"> |
允许用户输入文本 |
FlatList
与ScrollView区别:使用了虚拟列表,只渲染屏幕可见的元素
import React from 'react';
import {FlatList, StyleSheet, Text, View} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22,
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
});
const FlatListBasics = () => {
return (
<View style={styles.container}>
<FlatList
data={[
{key: 'Devin'},
{key: 'Dan'},
{key: 'Dominic'},
{key: 'Jackson'},
{key: 'James'},
{key: 'Joel'},
{key: 'John'},
{key: 'Jillian'},
{key: 'Jimmy'},
{key: 'Julie'},
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
);
};
export default FlatListBasics;
平台相关代码
手动判断当前平台
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100,
});
自动选择平台
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
...Platform.select({
ios: {
backgroundColor: 'red',
},
android: {
backgroundColor: 'green',
},
default: {
// other platforms, web for example
backgroundColor: 'blue',
},
}),
},
});
不同平台选择不同组件
const Component = Platform.select({
ios: () => require('ComponentIOS'),
android: () => require('ComponentAndroid'),
})();
<Component />;
const Component = Platform.select({
native: () => require('ComponentForNative'),
default: () => require('ComponentForWeb'),
})();
<Component />;
查看当前平台版本
Android:
import {Platform} from 'react-native';
if (Platform.Version === 25) {
console.log('Running on Nougat!');
}
IOS:
import {Platform} from 'react-native';
const majorVersionIOS = parseInt(Platform.Version, 10);
if (majorVersionIOS <= 9) {
console.log('Work around a change in behavior');
}
通过文件名区别平台
BigButton.ios.js
BigButton.android.js
import BigButton from './BigButton'; // 自动选择平台
样式
使用StyleSheet创建
import React from 'react';
import {StyleSheet, Text, View} from 'react-native';
const LotsOfStyles = () => {
return (
<View style={styles.container}>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigBlue}>just bigBlue</Text>
<Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 50,
},
bigBlue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
export default LotsOfStyles;
Flex布局
与Web的区别:flexDirection在RN中默认为column ,而web是row
| 特性 | React Native | Web (CSS Flexbox) |
|---|---|---|
| 默认主轴方向 | column 纵向 📌 | row 横向 📌 |
| Flex 基准单位 | 直接表示权重(分配剩余空间) | 表示增长倍数,受 flex-basis 影响 |
| 宽高计算方式 | 依赖父容器实际尺寸(必须有高度) | 默认可以自适应内容大小 |
| 支持样式属性 | Flex subset,少很多 | Full CSS Flexbox spec |
| 媒体查询等布局能力 | ❌ 不支持 CSS | ✔ 强大 CSS 特性 |
| 渲染引擎 | Yoga 布局引擎 | 浏览器 CSS 引擎 |
| 文本布局 | Text 是独立节点,需要不同布局规则 | Inline text 默认布局 |
flex属性汇总
| 属性名 | 作用简述 | 可选值 / 类型 | 与 Web 的差异 |
|---|---|---|---|
| display | 启用 flex 布局 | flex |
RN 默认就是 flex |
| flex | 分配剩余空间的比例 | number | RN 更简单:不依赖 basis |
| flexGrow | 可扩展比例 | number | 相同 |
| flexShrink | 缩小比例 | number | 相同 |
| flexBasis | 初始占用空间 | number / string | RN 少数支持百分比情况有限 |
| flexDirection | 主轴方向 | row / column / row-reverse / column-reverse |
默认 column(Web 默认 row) |
| flexWrap | 是否换行 | nowrap / wrap / wrap-reverse |
行为不完全等同 Web |
| justifyContent | 主轴对齐 | flex-start / center / flex-end / space-between / space-around / space-evenly |
相同 |
| alignItems | 交叉轴对齐 | flex-start / center / flex-end / stretch / baseline |
baseline 支持较弱 |
| alignSelf | 单个子元素在交叉轴对齐 | 与 alignItems 同 | 相同 |
| alignContent | 多行对齐 | 与 justifyContent 同 | 多行场景有限 |
| position | 定位方式 | relative / absolute |
只有这两种 |
| top / left / bottom / right | 绝对定位偏移 | number / % | 与 Web 基本一致 |
| rowGap / columnGap / gap | 子元素间距 | ❌不支持(除新版本部分实现) | Web 支持较好 |
图片资源
<Image source={require('./my-icon.png')} />
Image 和 **ImageBackground**区别:
| 特性 | Image | ImageBackground |
|---|---|---|
| 主要用途 | 显示一张图片 | 显示图片并作为背景容器 |
| 是否能放子元素 | ❌ 不能 | ✔ 可以放任意子组件 |
| 常用场景 | 图标、头像、封面图 | 背景图、Banner、带文本覆盖的图片 |
| 布局特性 | 作为“内容元素” | 作为“布局容器” |
| API/Props | 单一图片配置 | 在 Image 基础上增加 children 渲染功能 |
按钮
| 组件 | 自定义样式 | 点击视觉反馈 | 支持状态 | 平台 | 用法特点 / 场景 |
|---|---|---|---|---|---|
| Button | ❌ 很有限 | ✔ 默认系统样式 | 只有 onPress | iOS/Android | 最简单快速的按钮,无需自定义样式;只适合纯按钮 |
| TouchableOpacity | ✔ | ✔ 点击透明度变化 | pressed / onPress / onLongPress | iOS/Android | 最常用,简单渐隐点击效果;可包裹任意元素 |
| TouchableHighlight | ✔ | ✔ 背景高亮(变暗) | pressed / onPress / onLongPress | iOS/Android | 点击时有高亮覆盖效果;列表或强调点击反馈常用 |
| TouchableNativeFeedback | ✔ | ✔ 波纹效果 | pressed / onPress / onLongPress | Android 专属 | 安卓原生 Material 波纹效果,适合 Android 风格交互 |
| TouchableWithoutFeedback | ✔ | ❌ 无视觉反馈 | pressed / onPress / onLongPress | iOS/Android | 点击不显示任何反馈,适合自定义动画或处理复杂触控 |
| Pressable | ✔(动态样式) | ✔ 可自定义 | pressed / hover / focus / onPress / onLongPress / onPressIn / onPressOut | iOS/Android/Web | 统一替代 Touchable 系列,支持多状态,可根据状态动态样式,推荐新项目使用 |
动画
import React, {useEffect, useRef, type PropsWithChildren} from 'react';
import {Animated, Text, View, type ViewStyle} from 'react-native';
type FadeInViewProps = PropsWithChildren<{style: ViewStyle}>;
const FadeInView: React.FC<FadeInViewProps> = props => {
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 10000,
useNativeDriver: true,
}).start();
}, [fadeAnim]);
return (
<Animated.View // Special animatable View
style={{
...props.style,
opacity: fadeAnim, // Bind opacity to animated value
}}>
{props.children}
</Animated.View>
);
};
// You can then use your `FadeInView` in place of a `View` in your components:
export default () => {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<FadeInView
style={{
width: 250,
height: 50,
backgroundColor: 'powderblue',
}}>
<Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>
Fading in
</Text>
</FadeInView>
</View>
);
};
手势应答
| 回调函数 | 触发时机 | 返回值/作用 | 说明 |
|---|---|---|---|
onStartShouldSetResponder |
手指按下 | true/false |
是否愿意成为 responder(接收触摸事件) |
onMoveShouldSetResponder |
手指移动 | true/false |
是否在手势移动时成为 responder |
onResponderGrant |
获得响应权 | — | 组件成为 responder 时触发(手势开始) |
onResponderMove |
手指移动 | — | 手势移动过程中持续触发 |
onResponderRelease |
手指抬起 | — | 手势结束(释放) |
onResponderTerminate |
响应被抢占 | — | 另一个组件抢占 responder 权限时触发 |
onResponderTerminationRequest |
响应抢占请求 | true/false |
是否允许被抢占,返回 true 则允许,返回 false 拒绝抢占 |
onStartShouldSetResponderCapture |
手指按下(捕获阶段) | true/false |
捕获阶段判断是否想成为 responder(阻止子组件响应) |
onMoveShouldSetResponderCapture |
手指移动(捕获阶段) | true/false |
捕获阶段判断是否想成为 responder(阻止子组件响应) |
evt是一个具有以下形式的合成触摸事件:
- nativeEvent
changedTouches- 自上次事件以来所有已更改的触摸事件数组identifier- 触摸的 IDlocationX- 触摸点相对于元素的 X 坐标locationY- 触摸点相对于元素的 Y 坐标pageX- 触摸点相对于根元素的 X 坐标pageY- 触摸点相对于根元素的 Y 坐标target- 接收到触摸事件的元素的节点 IDtimestamp- 用于记录触摸事件的时间标识符,可用于速度计算touches- 屏幕上所有当前触摸点的数组
<View
style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}
onStartShouldSetResponder={() => true} // 想成为 responder
onResponderGrant={() => console.log('Gesture started')}
onResponderMove={evt => console.log('Moving:', evt.nativeEvent)}
onResponderRelease={() => console.log('Gesture ended')}
onResponderReject={()=>{
console.log("rejected");
}}
>
<Text>Touch me!</Text>
</View>