React Native 不是"把 HTML 写进手机"。它使用 React 的组件模型和状态模型,但渲染目标是 Android 和 iOS 的原生视图。学习 React Native 的第一步,是把 Web 心智切换到移动端心智:没有 DOM、没有 CSS 完整层叠模型、没有浏览器布局默认行为,只有平台视图、触摸交互、设备尺寸、原生能力和跨平台差异。
1. React Native 解决什么问题
React Native 让你用 JavaScript 和 React 描述移动应用 UI,同时运行时创建 Android / iOS 原生视图。
text
React Component -> React Native Renderer -> Native View
例如:
jsx
import { Text, View } from 'react-native';
export default function App() {
return (
<View>
<Text>Hello React Native</Text>
</View>
);
}
View 不是 div,Text 不是 p。它们最终映射到平台原生控件。
2. 与 React Web 的关键差异
| 维度 | React Web | React Native |
|---|---|---|
| 渲染目标 | DOM | Android/iOS 原生视图 |
| 文本 | 任意标签内可放文本 | 文本必须放在 Text 内 |
| 样式 | CSS、class、层叠 | JS 对象、StyleSheet、有限 CSS 子集 |
| 布局 | block、inline、flex、grid | 默认 Flexbox,主轴默认 column |
| 事件 | 鼠标、键盘、浏览器事件 | touch、press、gesture、平台事件 |
| 导航 | URL / Router | Stack、Tabs、Deep Link |
| 资源 | 浏览器加载 | Metro bundler、原生资源 |
| 平台能力 | Web API | Native Module、Expo API、平台 SDK |
3. 核心组件地图
React Native 官方核心组件可按场景理解:
基础组件:
View:容器,类似非滚动布局块。Text:文本展示。Image:图片。TextInput:输入框。Pressable:触摸交互容器。ScrollView:滚动容器。StyleSheet:样式声明。
界面组件:
Button:基础按钮。Switch:布尔开关。ActivityIndicator:加载指示器。Modal:模态层。Alert:系统弹窗。StatusBar:状态栏控制。
列表:
FlatList:高性能列表。SectionList:分组列表。VirtualizedList:底层虚拟列表。
设备和平台:
Dimensions:屏幕尺寸。PixelRatio:像素密度。KeyboardAvoidingView:键盘避让。Linking:链接和 Deep Link。BackHandler:Android 返回键。PermissionsAndroid:Android 权限。ActionSheetIOS:iOS Action Sheet。ToastAndroid:Android Toast。
4. View
View 是最基础的容器。
jsx
function Card({ children }) {
return <View style={styles.card}>{children}</View>;
}
const styles = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
backgroundColor: '#FFFFFF',
},
});
注意:
View不能直接放字符串。- 触摸交互优先用
Pressable。 - 阴影、边框、圆角在 Android/iOS 上表现不同。
5. Text
文本必须放在 Text 中。
jsx
function Title({ children }) {
return <Text style={styles.title}>{children}</Text>;
}
嵌套文本:
jsx
<Text>
React Native <Text style={styles.emphasis}>移动端体系</Text>
</Text>
常见属性:
numberOfLinesellipsizeModeselectableallowFontScalingonPress
jsx
<Text numberOfLines={2} ellipsizeMode="tail">
一段很长的课程简介...
</Text>
6. Image
本地图片:
jsx
<Image source={require('./assets/logo.png')} style={styles.logo} />
远程图片:
jsx
<Image
source={{ uri: 'https://example.com/avatar.png' }}
style={styles.avatar}
/>
必须给明确尺寸:
jsx
const styles = StyleSheet.create({
avatar: {
width: 48,
height: 48,
borderRadius: 24,
},
});
移动端图片要关注:
- 分辨率和像素密度。
- 缓存策略。
- 占位图。
- 失败状态。
- 大图内存。
7. Pressable
Pressable 是现代触摸交互基础组件。
jsx
function PrimaryButton({ children, onPress }) {
return (
<Pressable
onPress={onPress}
style={({ pressed }) => [
styles.button,
pressed && styles.buttonPressed,
]}
>
<Text style={styles.buttonText}>{children}</Text>
</Pressable>
);
}
相比 TouchableOpacity,Pressable 能表达更多交互阶段:
pressedhoveredfocused
8. TextInput
jsx
function SearchInput({ value, onChangeText }) {
return (
<TextInput
value={value}
onChangeText={onChangeText}
placeholder="搜索知识点"
returnKeyType="search"
style={styles.input}
/>
);
}
常用属性:
valueonChangeTextplaceholderkeyboardTypesecureTextEntryreturnKeyTypeautoCapitalizemultiline
移动端表单必须考虑键盘遮挡、输入法、自动大写、密码安全和提交按钮。
9. ScrollView
ScrollView 会一次性渲染所有子元素。
jsx
<ScrollView contentContainerStyle={styles.content}>
{items.map((item) => (
<KnowledgeCard key={item.id} item={item} />
))}
</ScrollView>
适合:
- 内容较少。
- 静态页面。
- 表单页。
不适合:
- 长列表。
- 无限滚动。
- 数据量不可控列表。
长列表优先使用 FlatList。
10. FlatList
jsx
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <KnowledgeCard item={item} />}
/>
常用优化:
jsx
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={renderItem}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={7}
removeClippedSubviews
/>
专家提醒:
renderItem尽量稳定。keyExtractor必须稳定。- 行高固定时使用
getItemLayout。 - 大列表避免内联匿名组件和复杂阴影。
11. SectionList
适合分组列表。
jsx
<SectionList
sections={[
{ title: '入门', data: beginnerItems },
{ title: '进阶', data: advancedItems },
]}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <KnowledgeCard item={item} />}
renderSectionHeader={({ section }) => (
<Text style={styles.sectionTitle}>{section.title}</Text>
)}
/>
12. StyleSheet
jsx
const styles = StyleSheet.create({
screen: {
flex: 1,
backgroundColor: '#F4F7F8',
padding: 16,
},
title: {
fontSize: 24,
fontWeight: '700',
color: '#172026',
},
});
React Native 样式是 JS 对象,不是 CSS:
- 使用 camelCase。
- 数字通常表示 dp。
- 没有完整 CSS 选择器。
- 没有继承式 className。
- 默认主轴是 column。
13. Flexbox 布局
React Native 默认:
text
flexDirection: 'column'
常见布局:
jsx
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 12,
},
});
占满屏幕:
jsx
screen: {
flex: 1,
}
移动端布局要考虑:
- 小屏幕。
- 横竖屏。
- 刘海屏。
- 字体缩放。
- Android/iOS 默认差异。
14. Safe Area
iPhone 刘海、动态岛、Android 状态栏都可能遮挡内容。
Expo / community 常用:
jsx
import { SafeAreaView } from 'react-native-safe-area-context';
function Screen() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Content />
</SafeAreaView>
);
}
基础 RN 也有 SafeAreaView,但实际项目更常使用 react-native-safe-area-context。
15. Platform
平台差异:
jsx
import { Platform } from 'react-native';
const styles = StyleSheet.create({
card: {
padding: 16,
...Platform.select({
ios: {
shadowColor: '#000',
shadowOpacity: 0.12,
shadowRadius: 8,
},
android: {
elevation: 3,
},
}),
},
});
文件级差异:
text
Button.ios.tsx
Button.android.tsx
16. Dimensions 与响应式
jsx
const { width } = useWindowDimensions();
const columns = width > 700 ? 2 : 1;
不要只按机型判断。移动端响应式要结合:
- 屏幕宽度。
- 横竖屏。
- 字体缩放。
- 平板。
- 分屏模式。
17. PixelRatio
jsx
const hairlineWidth = StyleSheet.hairlineWidth;
图片资源要考虑不同像素密度:
text
icon.png
icon@2x.png
icon@3x.png
18. 基础 Demo
jsx
import { Pressable, StyleSheet, Text, View } from 'react-native';
export function ComponentBasicsDemo() {
const [count, setCount] = React.useState(0);
return (
<View style={styles.card}>
<Text style={styles.title}>React Native 基础</Text>
<Text style={styles.summary}>View、Text、Pressable 与 StyleSheet。</Text>
<Pressable style={styles.button} onPress={() => setCount(count + 1)}>
<Text style={styles.buttonText}>点击 {count}</Text>
</Pressable>
</View>
);
}
19. 入门达标标准
你应该能独立完成:
- 用
View、Text、Image、Pressable组成页面。 - 用
StyleSheet写清晰样式。 - 用 Flexbox 做移动端布局。
- 正确处理文本、图片、输入框和滚动容器。
- 知道什么时候用
ScrollView,什么时候用FlatList。 - 处理基础平台差异和 Safe Area。
20. 核心组件扩展清单
View、Text、Image、TextInput、Pressable 是最小基础,但真实项目还会高频使用:
ActivityIndicator:局部 loading。Modal:全屏或局部弹层。Alert:系统确认弹窗。Switch:布尔配置。StatusBar:状态栏颜色和内容模式。KeyboardAvoidingView:键盘遮挡处理。RefreshControl:下拉刷新。
Modal 示例:
jsx
<Modal visible={open} animationType="slide" onRequestClose={close}>
<View style={styles.modal}>
<Text style={styles.title}>确认删除</Text>
<Pressable onPress={close}>
<Text>关闭</Text>
</Pressable>
</View>
</Modal>
Android 必须处理 onRequestClose。
21. 样式差异扩展
React Native 样式不是 CSS。常见差异:
- 没有 CSS selector。
- 没有 cascade。
- 默认单位是 dp,不写 px。
zIndex在 Android/iOS 上和 Web 不完全一致。- 阴影平台差异明显。
gap需要确认 RN 版本支持。- 字体族需要平台配置。
阴影跨平台:
jsx
const shadow = Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 10,
},
android: {
elevation: 4,
},
});
22. 移动端布局边界
移动端布局要额外考虑:
- 小屏设备。
- 大屏平板。
- 横屏。
- 分屏。
- 字体缩放。
- 刘海和状态栏。
- 底部手势条。
- 键盘弹出。
响应式示例:
jsx
function useColumns() {
const { width } = useWindowDimensions();
if (width >= 900) return 3;
if (width >= 600) return 2;
return 1;
}
23. Text 深水区
移动端文本不是简单字号:
allowFontScaling影响可访问性。- Android 字体行高和 iOS 不同。
- 自定义字体需要原生资源配置。
- 多语言会让文本长度变化。
numberOfLines需要配合布局宽度。
jsx
<Text
maxFontSizeMultiplier={1.4}
numberOfLines={2}
accessibilityRole="header"
>
React Native 知识体系
</Text>
24. Image 深水区
图片必须关注内存:
- 列表中不要加载原图。
- 使用缩略图。
- 明确 width/height。
- 处理失败状态。
- 重要图片可预加载。
- 头像和封面要区分缓存策略。
图片错误:
jsx
<Image source={{ uri }} style={{ flex: 1 }} />
没有明确高度时可能不显示或布局异常。
25. 入门阶段反模式
- 在
View里直接写字符串。 - 用
ScrollView渲染几百条数据。 - 图片不设置尺寸。
- 点击区域太小。
- 不处理 Safe Area。
- Web CSS 心智照搬到 RN。
- 忽略 Android/iOS 样式差异。
- 表单页不处理键盘遮挡。
26. 基础评审题
- 文本是否都放在
Text中? - 长列表是否使用
FlatList? - 图片是否有明确尺寸?
- 点击目标是否足够大?
- 是否适配 Safe Area?
- 是否考虑字体缩放?
- 是否有平台差异代码?
- 样式是否使用 theme token?
27. React Native 基础知识点索引
View是基础容器。Text承载所有文本。Image必须有尺寸。TextInput使用onChangeText。Pressable是现代触摸基础。ScrollView一次性渲染子元素。FlatList适合长列表。SectionList适合分组。StyleSheet是样式对象。- 默认 flexDirection 是 column。
- Safe Area 必须考虑。
- 状态栏和底部手势条会遮挡内容。
- Android/iOS 阴影不同。
- Android 返回键需要处理。
- 字体缩放影响布局。
- 点击区域要足够大。
- 平台差异可用
Platform.select。 - 图片资源要考虑 @2x/@3x。
- 键盘会影响表单布局。
- 无障碍不是可选项。
28. 基础练习扩展
- 实现一个课程卡片。
- 实现一个搜索输入框。
- 实现一个分组列表。
- 实现一个 Modal。
- 实现一个可访问按钮。
- 实现一个响应式两列布局。
- 实现一个键盘避让表单。
- 实现 iOS/Android 阴影差异。
面试题完整答案总集:React Native 基础、组件与布局
文本是否都放在 Text 中?
React Native 中字符串不能直接放在 View 里,必须放在 Text 中。原因是 React Native 渲染目标是原生视图,文本节点需要映射到平台文本组件。直接把字符串放进 View 会报错或表现异常。
长列表是否应该使用 FlatList?
是。ScrollView 会一次性渲染所有子元素,数据量大时会造成内存和渲染压力。FlatList 基于虚拟化,只渲染可视窗口附近的内容,适合长列表、分页、无限滚动和下拉刷新。
图片为什么必须有明确尺寸?
React Native 没有浏览器图片自动占位布局逻辑。远程图片如果没有 width/height,可能无法正确显示或导致布局抖动。明确尺寸还能降低布局计算成本,并帮助控制内存。
点击目标为什么要足够大?
移动端是手指触摸,不是鼠标精确点击。点击区域过小会导致误触和可访问性问题。通常按钮和图标应使用足够 padding 或 hitSlop 扩大触摸区域。
为什么必须适配 Safe Area?
iPhone 刘海、动态岛、Android 状态栏和底部手势条都可能遮挡内容。Safe Area 能保证内容不被系统区域覆盖,尤其是顶部导航、底部按钮和全屏页面。
字体缩放为什么会影响布局?
用户可能在系统中开启大字体。若布局高度固定或文本容器太窄,字体缩放会导致文字截断、按钮溢出或布局重叠。关键文本应允许合理缩放,并用弹性布局承载。
如何处理 Android/iOS 样式差异?
用 Platform.select 或平台文件区分。典型差异包括阴影、字体、状态栏、返回键、Modal 行为和某些组件默认样式。跨平台组件应把差异封装在基础组件或 theme 中。
为什么样式要使用 theme token?
theme token 能统一颜色、间距、字号、圆角和阴影,避免页面各写各的。它让设计调整、暗色模式、品牌换肤和组件库治理更容易,也能减少视觉不一致。