React Native 学习指南
什么是 React Native
React Native 是使用 React 构建原生移动应用的框架,由 Facebook(Meta)开发。它允许你使用 JavaScript 和 React 编写真正的原生移动应用。
核心特点
- 跨平台:一套代码同时运行在 iOS 和 Android
- 原生性能:使用真正的原生组件,不是 WebView
- 热重载:修改代码立即看到效果,无需重新编译
- React 语法:如果你会 React,就能快速上手
- 庞大生态:丰富的第三方库和社区支持
- 原生扩展:可以编写原生模块扩展功能
与其他方案对比
| 特性 | React Native | Flutter | 原生开发 |
|---|---|---|---|
| 语言 | JavaScript | Dart | Swift/Kotlin |
| 性能 | 接近原生 | 接近原生 | 最优 |
| 学习曲线 | 低(会React即可) | 中等 | 高 |
| 代码复用 | 高 | 高 | 低 |
| 社区 | 庞大 | 快速增长 | 成熟 |
环境搭建
macOS 开发环境
1. 安装 Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. 安装 Node.js
# 使用 Homebrew 安装
brew install node
# 或使用 nvm(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install --lts
nvm use --lts
3. 安装 Watchman
brew install watchman
4. iOS 开发环境
# 安装 Xcode(从 App Store)
# 安装 Xcode Command Line Tools
xcode-select --install
# 安装 CocoaPods
sudo gem install cocoapods
5. Android 开发环境
# 下载并安装 Android Studio
# https://developer.android.com/studio
# 配置环境变量(添加到 ~/.zshrc 或 ~/.bash_profile)
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools
Windows 开发环境
1. 安装 Node.js
从 nodejs.org 下载并安装 LTS 版本
2. 安装 JDK
# 使用 Chocolatey
choco install -y microsoft-openjdk11
3. Android 开发环境
- 下载并安装 Android Studio
- 配置 ANDROID_HOME 环境变量
- 安装 Android SDK
创建第一个项目
# 使用 React Native CLI(推荐)
npx react-native init MyFirstApp
# 进入项目目录
cd MyFirstApp
# 查看项目结构
ls -la
项目结构
MyFirstApp/
├── android/ # Android 原生代码
├── ios/ # iOS 原生代码
├── node_modules/ # 依赖包
├── App.tsx # 应用入口组件
├── index.js # 应用注册入口
├── package.json # 项目配置
├── tsconfig.json # TypeScript 配置
└── metro.config.js # Metro 打包配置
运行项目
iOS
# 方式1:使用命令行
npx react-native run-ios
# 方式2:指定模拟器
npx react-native run-ios --simulator="iPhone 14 Pro"
# 方式3:使用 Xcode
# 打开 ios/MyFirstApp.xcworkspace
# 点击运行按钮
Android
# 启动 Android 模拟器(先在 Android Studio 中创建)
# 方式1:使用命令行
npx react-native run-android
# 方式2:使用 Android Studio
# 打开 android 目录
# 点击运行按钮
真机调试
iOS 真机
- 使用数据线连接 iPhone
- 在 Xcode 中选择你的设备
- 配置开发者证书
- 点击运行
Android 真机
- 开启开发者模式和 USB 调试
- 使用数据线连接手机
- 运行
adb devices确认设备已连接 - 运行
npx react-native run-android
常见问题
端口被占用
# 杀死占用 8081 端口的进程
lsof -ti:8081 | xargs kill
# 或指定其他端口
npx react-native start --port 8082
Metro 打包服务器未启动
# 手动启动
npx react-native start
# 清除缓存
npx react-native start --reset-cache
iOS 编译失败
# 清理构建
cd ios
rm -rf Pods Podfile.lock
pod install
cd ..
# 重新运行
npx react-native run-ios
Android 编译失败
# 清理构建
cd android
./gradlew clean
cd ..
# 重新运行
npx react-native run-android
基础组件
View
import { View, StyleSheet } from 'react-native';
function App() {
return (
<View style={styles.container}>
<View style={styles.box} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff'
},
box: {
width: 100,
height: 100,
backgroundColor: 'blue'
}
});
Text
import { Text, StyleSheet } from 'react-native';
function App() {
return (
<Text style={styles.text}>
Hello React Native
</Text>
);
}
const styles = StyleSheet.create({
text: {
fontSize: 20,
color: '#333',
fontWeight: 'bold'
}
});
Image
import { Image } from 'react-native';
// 本地图片
<Image source={require('./assets/logo.png')} style={{ width: 100, height: 100 }} />
// 网络图片
<Image source={{ uri: 'https://example.com/image.jpg' }} style={{ width: 100, height: 100 }} />
ScrollView
import { ScrollView, Text } from 'react-native';
function App() {
return (
<ScrollView>
<Text>Item 1</Text>
<Text>Item 2</Text>
<Text>Item 3</Text>
</ScrollView>
);
}
FlatList
import { FlatList, Text, View } from 'react-native';
function App() {
const data = [
{ id: '1', title: 'Item 1' },
{ id: '2', title: 'Item 2' },
{ id: '3', title: 'Item 3' }
];
return (
<FlatList
data={data}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View>
<Text>{item.title}</Text>
</View>
)}
/>
);
}
TextInput
import { TextInput, StyleSheet } from 'react-native';
import { useState } from 'react';
function App() {
const [text, setText] = useState('');
return (
<TextInput
style={styles.input}
value={text}
onChangeText={setText}
placeholder="请输入"
/>
);
}
const styles = StyleSheet.create({
input: {
height: 40,
borderWidth: 1,
borderColor: '#ccc',
paddingHorizontal: 10
}
});
Button
import { Button, Alert } from 'react-native';
function App() {
return (
<Button
title="点击我"
onPress={() => Alert.alert('提示', '按钮被点击')}
color="#007AFF"
/>
);
}
TouchableOpacity
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
function App() {
return (
<TouchableOpacity style={styles.button} onPress={() => console.log('pressed')}>
<Text style={styles.text}>自定义按钮</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8
},
text: {
color: '#fff',
textAlign: 'center'
}
});
样式
StyleSheet
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5'
},
text: {
fontSize: 18,
color: '#333'
}
});
Flexbox
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row', // column | row
justifyContent: 'center', // flex-start | flex-end | center | space-between | space-around
alignItems: 'center' // flex-start | flex-end | center | stretch
}
});
导航
React Navigation
yarn add @react-navigation/native
yarn add react-native-screens react-native-safe-area-context
yarn add @react-navigation/native-stack
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
学习路径
- 基础组件 → View、Text、Image
- 列表 → FlatList、SectionList
- 样式 → StyleSheet、Flexbox
- 导航 → React Navigation
- 网络请求 → Fetch、Axios
- 状态管理 → Redux、Zustand
- 原生模块 → 桥接原生代码
- 性能优化 → 列表优化、图片优化
- 打包发布 → iOS、Android 发布流程
第一个应用
Hello World
// App.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
function App() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello, React Native!</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5'
},
text: {
fontSize: 24,
fontWeight: 'bold',
color: '#333'
}
});
export default App;
计数器应用
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
function Counter() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.title}>计数器</Text>
<Text style={styles.count}>{count}</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={() => setCount(count - 1)}
>
<Text style={styles.buttonText}>-</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => setCount(0)}
>
<Text style={styles.buttonText}>重置</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => setCount(count + 1)}
>
<Text style={styles.buttonText}>+</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
padding: 20
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 20,
color: '#333'
},
count: {
fontSize: 72,
fontWeight: 'bold',
color: '#007AFF',
marginBottom: 40
},
buttonContainer: {
flexDirection: 'row',
gap: 15
},
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 30,
paddingVertical: 15,
borderRadius: 10,
minWidth: 80,
alignItems: 'center'
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold'
}
});
export default Counter;
Todo List 应用
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
FlatList,
StyleSheet,
KeyboardAvoidingView,
Platform
} from 'react-native';
function TodoApp() {
const [text, setText] = useState('');
const [todos, setTodos] = useState([]);
const addTodo = () => {
if (text.trim()) {
setTodos([
...todos,
{ id: Date.now().toString(), text: text.trim(), done: false }
]);
setText('');
}
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<Text style={styles.title}>待办事项</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
value={text}
onChangeText={setText}
placeholder="添加新任务..."
onSubmitEditing={addTodo}
/>
<TouchableOpacity style={styles.addButton} onPress={addTodo}>
<Text style={styles.addButtonText}>添加</Text>
</TouchableOpacity>
</View>
<FlatList
data={todos}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={styles.todoItem}>
<TouchableOpacity
style={styles.todoContent}
onPress={() => toggleTodo(item.id)}
>
<View style={[
styles.checkbox,
item.done && styles.checkboxChecked
]}>
{item.done && <Text style={styles.checkmark}>✓</Text>}
</View>
<Text style={[
styles.todoText,
item.done && styles.todoTextDone
]}>
{item.text}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => deleteTodo(item.id)}
>
<Text style={styles.deleteButtonText}>删除</Text>
</TouchableOpacity>
</View>
)}
ListEmptyComponent={
<Text style={styles.emptyText}>暂无待办事项</Text>
}
/>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
padding: 20,
paddingTop: 60
},
title: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 20,
color: '#333'
},
inputContainer: {
flexDirection: 'row',
marginBottom: 20
},
input: {
flex: 1,
backgroundColor: '#fff',
paddingHorizontal: 15,
paddingVertical: 12,
borderRadius: 8,
fontSize: 16,
marginRight: 10
},
addButton: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
justifyContent: 'center'
},
addButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold'
},
todoItem: {
flexDirection: 'row',
backgroundColor: '#fff',
padding: 15,
borderRadius: 8,
marginBottom: 10,
alignItems: 'center'
},
todoContent: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
checkbox: {
width: 24,
height: 24,
borderRadius: 12,
borderWidth: 2,
borderColor: '#007AFF',
marginRight: 12,
justifyContent: 'center',
alignItems: 'center'
},
checkboxChecked: {
backgroundColor: '#007AFF'
},
checkmark: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold'
},
todoText: {
fontSize: 16,
color: '#333',
flex: 1
},
todoTextDone: {
textDecorationLine: 'line-through',
color: '#999'
},
deleteButton: {
backgroundColor: '#FF3B30',
paddingHorizontal: 15,
paddingVertical: 8,
borderRadius: 6
},
deleteButtonText: {
color: '#fff',
fontSize: 14,
fontWeight: 'bold'
},
emptyText: {
textAlign: 'center',
color: '#999',
fontSize: 16,
marginTop: 40
}
});
export default TodoApp;
调试技巧
开发者菜单
- iOS 模拟器:
Cmd + D - Android 模拟器:
Cmd + M(Mac) 或Ctrl + M(Windows) - 真机:摇一摇设备
Chrome DevTools
- 打开开发者菜单
- 选择 "Debug"
- 在 Chrome 中打开
http://localhost:8081/debugger-ui - 打开 Chrome DevTools(F12)
React DevTools
# 安装
yarn global add react-devtools
# 运行
react-devtools
日志输出
// 基础日志
console.log('普通日志');
console.warn('警告');
console.error('错误');
// 对象日志
console.log('用户数据:', { name: 'John', age: 25 });
// 性能测试
console.time('操作耗时');
// ... 执行操作
console.timeEnd('操作耗时');
Flipper 调试工具
# 下载并安装 Flipper
# https://fbflipper.com/
# 功能:
# - 查看网络请求
# - 查看 AsyncStorage
# - 查看布局层级
# - 性能监控
学习路径
第一阶段:基础入门(1-2周)
- 环境搭建:配置开发环境
- 核心组件:View、Text、Image、ScrollView
- 样式系统:StyleSheet、Flexbox
- 用户交互:TouchableOpacity、Button、TextInput
- 列表渲染:FlatList、SectionList
第二阶段:进阶学习(2-3周)
- 导航系统:React Navigation
- 状态管理:Context API、Redux
- 网络请求:Fetch、Axios
- 本地存储:AsyncStorage
- 表单处理:表单验证、键盘处理
第三阶段:实战项目(3-4周)
- 完整应用:Todo App、天气应用、新闻应用
- 第三方库:图标、图片选择、地图
- 性能优化:列表优化、图片优化
- 原生模块:桥接原生代码
第四阶段:发布上线(1-2周)
- 打包配置:图标、启动屏、权限
- iOS 发布:证书、App Store
- Android 发布:签名、Google Play
- 持续集成:自动化构建