CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React Native 开发入门/课时/核心组件
RawBlame
课时目录12 节

React Native 开发入门

  1. 01RN 与 Expo
  2. 02核心组件
  3. 03布局与列表
  4. 04平台差异
  5. 05导航与手势
  6. 06State 与 JSX
  7. 07网络
  8. 08本地存储
  9. 09原生模块
  10. 10动画
  11. 11性能优化
  12. 12测试与发布

核心组件

View/Text/Pressable。

30 分钟

核心组件与样式

这一课你将能做到

  1. 用 View、Text、Image、TextInput、ScrollView、Pressable 搭建任意静态界面
  2. 写出能响应按压状态的可点击组件,并说明 Pressable 和 Button 的本质区别
  3. 用 StyleSheet.create 管理样式,理解 flex: 1 在 RN 布局中的核心含义

为什么要学

RN 里没有 HTML 标签,所有界面都靠十几个核心组件像乐高一样拼出来。这几个组件就像积木的基础块——块不多,但组合无限。掌握它们,你就掌握了 RN 界面的 80%。

人话:View 是万能容器(相当于 div),Text 是文字容器(所有文字必须用它包),Image 是图片,TextInput 是输入框,ScrollView 是滚动容器。Pressable 是「可按压的任何东西」——RN 里真正的按钮全部是自己用 Pressable 包出来的,Button 组件仅用于极快速的原型验证。

先建立图像

import { View, Text, Image, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.card}>
        <Image source={{ uri: 'https://picsum.photos/100' }} style={styles.avatar} />
        <View style={styles.info}>
          <Text style={styles.name}>张小明</Text>
          <Text style={styles.bio}>前端工程师 · 北京</Text>
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5' },
  card: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#fff', padding: 16, borderRadius: 12, width: '90%' },
  avatar: { width: 56, height: 56, borderRadius: 28 },
  info: { marginLeft: 12 },
  name: { fontSize: 18, fontWeight: 'bold' },
  bio: { fontSize: 14, color: '#888', marginTop: 4 },
});

渲染结果:灰白屏幕中央一张白色卡片,左边圆形头像,右边姓名和简介。整个页面一个 div 没有,全是 RN 原生组件。

跟着做

步骤 1:View 与 Text 的分工与嵌套

View 只能装东西,不能直接显示文字;文字必须放 Text 里。这是 RN 的铁律,和 Web 最大的区别之一。

import { View, Text } from 'react-native';

export default function App() {
  return (
    <View style={{ padding: 24 }}>
      {/* 错误示范:文字直接写在 View 里会红屏 */}
      {/* <View>你好</View> */}

      {/* 正确 */}
      <Text style={{ fontSize: 20, fontWeight: 'bold' }}>标题</Text>
      <Text style={{ fontSize: 14, color: '#666', marginTop: 8 }}>
        普通文字。总价:<Text style={{ color: 'red', fontWeight: 'bold' }}>¥99</Text>
      </Text>
    </View>
  );
}

Text 可以嵌套 Text,子 Text 继承父 Text 的样式,但可以被覆盖——这是 RN 里唯一有样式继承关系的地方。View 嵌 View 则不存在继承。

步骤 2:Image 显示网络图和本地图

import { Image, View } from 'react-native';

export default function App() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      {/* 网络图片:必须显式给宽高,否则不显示 */}
      <Image
        source={{ uri: 'https://picsum.photos/200' }}
        style={{ width: 120, height: 120, borderRadius: 60 }}
        resizeMode="cover"
      />

      {/* 本地图片:require 静态引入,不需要写宽高(能自动读取尺寸) */}
      <Image
        source={require('./assets/icon.png')}
        style={{ width: 48, height: 48, marginTop: 16 }}
      />
    </View>
  );
}

网络图片必须给 style 里的 width 和 height,因为 RN 不会像浏览器那样下载后自适应布局。resizeMode 控制剪裁策略:cover(等比填充,可能裁切)、contain(等比完整显示)、stretch(拉伸填满,可能变形)。

步骤 3:TextInput 受控输入与常见属性

import { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';

export default function App() {
  const [name, setName] = useState('');
  const [password, setPassword] = useState('');

  return (
    <View style={styles.container}>
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="请输入用户名"
        autoCapitalize="none"
        style={styles.input}
      />
      <TextInput
        value={password}
        onChangeText={setPassword}
        placeholder="请输入密码"
        secureTextEntry
        style={styles.input}
      />
      <Text>你输入的用户名:{name}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24, justifyContent: 'center' },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 12,
    borderRadius: 8,
    marginBottom: 12,
    fontSize: 16,
  },
});

注意 onChangeText 直接接收字符串,不是 Web 的 onChange 事件对象。secureTextEntry 是密码框;autoCapitalize="none" 防止 iOS 首字母自动大写;keyboardType="email-address" 唤起带 @ 符号的键盘。

步骤 4:Pressable 做自定义按钮

import { Pressable, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Pressable
        onPress={() => alert('提交成功')}
        onPressIn={() => console.log('按下')}
        onPressOut={() => console.log('抬起')}
        hitSlop={16}   // 热区扩展 16dp,手指偏一点也能触发
        style={({ pressed }) => [
          {
            backgroundColor: pressed ? '#0056b3' : '#007bff',
            paddingVertical: 14,
            paddingHorizontal: 32,
            borderRadius: 8,
          },
        ]}
      >
        <Text style={{ color: '#fff', fontSize: 16, textAlign: 'center' }}>立即提交</Text>
      </Pressable>
    </View>
  );
}

style 可以传函数,参数里的 pressed 告诉你当前是否被按住,是实现按下变色效果的标准做法。hitSlop 扩展点击热区,对触控体验提升很大。还有 onLongPress、disabled、delayLongPress 等属性,满足绝大多数交互需求。

步骤 5:ScrollView 滚动与内容溢出

import { ScrollView, Text, View } from 'react-native';

export default function App() {
  return (
    <ScrollView
      style={{ flex: 1 }}
      contentContainerStyle={{ padding: 16, paddingBottom: 40 }}
      showsVerticalScrollIndicator={false}
    >
      {Array.from({ length: 30 }, (_, i) => (
        <Text key={i} style={{ paddingVertical: 12, borderBottomWidth: 1, borderColor: '#eee' }}>
          第 {i + 1} 行内容
        </Text>
      ))}
    </ScrollView>
  );
}

注意:style 控制 ScrollView 容器本身的大小,contentContainerStyle 控制内部内容的布局。如果内容被截断(比如最后一行没显示完),检查是否给 contentContainerStyle 加了底部 padding。

重要提醒:长列表不要用 ScrollView。ScrollView 会一次性把所有子元素全部渲染出来,100 条数据建 100 个原生视图,几百条就卡死。正式的长列表用下一课的 FlatList。

步骤 6:SafeAreaView 避开刘海和底部横条

import { SafeAreaView } from 'react-native-safe-area-context';
import { Text } from 'react-native';

export default function App() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <Text>内容不会被刘海、底部手势条遮挡</Text>
    </SafeAreaView>
  );
}

安装:npx expo install react-native-safe-area-context。注意要用社区这个库的版本,RN 自带的 SafeAreaView 只对 iOS 有效,Android 全面屏会出问题。

步骤 7:KeyboardAvoidingView 处理键盘遮挡

import { KeyboardAvoidingView, TextInput, Button, Platform } from 'react-native';

export default function App() {
  return (
    <KeyboardAvoidingView
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      style={{ flex: 1 }}
    >
      <TextInput
        placeholder="点我输入,键盘不会挡住"
        style={{ borderWidth: 1, padding: 12, margin: 24 }}
      />
      <Button title="提交" />
    </KeyboardAvoidingView>
  );
}

iOS 键盘弹起时内容自动上推,Android 用 height 行为。配合 Platform.OS 做平台区分,第 4 课会细讲。

讲清楚要点

  1. flex: 1 是布局的地基:RN 默认 Flexbox 布局,且主轴是纵向(column,和 Web 的默认 row 正好相反)。flex: 1 表示「占满父容器剩余空间」,根组件几乎都要写,否则内容被截断。
  2. 网络图片必须给尺寸:RN 不会像浏览器那样下载完成后自适应布局,不写宽高就是一片空白。本地图片用 require() 会自动读取尺寸。
  3. Button 组件只是个壳:<Button> 的样式死板且 iOS / Android 长得完全不同(iOS 是蓝色文字,Android 是绿色方块),实际项目全部用 Pressable 或 TouchableOpacity 自己封装,保证跨端一致性。
  4. StyleSheet.create 的价值:不仅集中管理样式,还允许 RN 做性能优化——样式对象只序列化一次传给原生侧。内联样式功能等价,但每次渲染都要重新传递。
  5. 样式没有继承:文字颜色、字号不会从父 View 传给子 Text,每个 Text 都得自己写 style。只有 Text 嵌套 Text 才有继承关系。
  6. 组件大小写敏感:View 是大写的,来自 react-native;写 view 小写会报错。这和 Web 的 HTML 标签大小写不敏感完全不同。

你可能会懵的地方

  • 为什么我的 justifyContent 方向「反了」? RN 默认 flexDirection: 'column'(纵向),所以 justifyContent: 'center' 是垂直居中,alignItems: 'center' 是水平居中。和 Web 默认的 row 正好相反,这是移动端绝大多数布局都是上下排列决定的。

  • 样式能写百分比吗? 宽高可以(width: '50%'),但 padding、margin 的百分比支持有限且行为不一致,推荐用 flex 布局代替百分比。

  • px 单位去哪了? RN 样式数字是无单位的「密度无关像素(dp / dip)」,系统自动适配不同分辨率屏幕。例如 fontSize: 16 在 320dp 宽的低分屏和 414dp 的 iPhone 上都是 16dp,但物理像素数不同,所以看起来大小一致。你不用操心,RN 已经做了适配。

  • TextInput 不输中文没事,一输中文光标乱跳? 这是受控模式下某些安卓机型的旧 bug。保持 value + onChangeText 的受控写法,RN 0.72+ 已基本修复。如果还遇到,可尝试在 onChangeText 里不直接同步回 value 而是用 useRef 暂存,失去焦点再同步。

  • Pressable 和 TouchableOpacity 到底用哪个? Pressable 是 RN 0.63 起官方推荐的新一代按压组件,API 更灵活(支持 pressed 状态、hitSlop、onPressIn/Out 细粒度回调);TouchableOpacity 是老 API,只提供透明度反馈,已处于维护模式。新项目一律用 Pressable。

错法对照

错法问题正解
<View>你好</View>红屏:Text strings must be within a Text component<View><Text>你好</Text></View>
网络 Image 不写宽高图片空白不显示显式 style={{ width: 120, height: 120 }}
用 onChange 接 TextInput拿到的是事件对象不是字符串用 onChangeText={(text) => ...}
长列表用 map + ScrollView全量渲染,几百条就卡死崩溃用 FlatList(下一课)
以为样式会从父 View 继承给子 Text子 Text 颜色字号全是默认每个 Text 单独写 style
ScrollView 内容被底部截断忘了它默认不自动撑开底部用 contentContainerStyle 加内边距
用 RN 自带的 SafeAreaViewAndroid 全面屏无效react-native-safe-area-context
按钮用 <Button title="确定" />样式不可控,两端长得不一样用 Pressable 封装统一样式

卡点急救

  1. 图片不显示:检查是否给了宽高;http(非 https)图片在 iOS 默认被拦截,换 https 或在 app.json 里配 infoPlist 例外。
  2. 键盘挡住输入框:用 KeyboardAvoidingView 包住页面,iOS 设 behavior="padding",Android 设 height 或 padding。
  3. Pressable 点了没反应:检查是不是包在 ScrollView 里被手势拦截了;或外层有绝对定位元素(zIndex 更高)遮挡。
  4. 组件报错 Invariant Violation: View config getter...:引入了一个不存在的组件名,检查拼写和 import 路径。
  5. 样式不生效:RN 样式不支持 float、display: block/inline、position: sticky 等 Web 概念。布局全部靠 Flexbox 和 position: 'absolute'。

面试常问

  • TouchableOpacity 和 Pressable 的区别? Pressable 是新一代官方推荐按压组件,API 更灵活(pressed 状态、onPressIn/onPressOut、hitSlop);TouchableOpacity 是旧 API,只提供透明度变化反馈,已不推荐使用。新项目用 Pressable。

  • StyleSheet.create 和内联样式的区别? 功能等价,但 StyleSheet.create 会做属性校验、去重,且样式对象只跨桥传递一次,性能更好,可读性和可维护性也更好。开发期建议全部用 StyleSheet.create。

  • RN 布局为什么只用 Flexbox? Flexbox 一套模型足以描述移动端绝大多数布局,Meta 开源的 Yoga 引擎可以高效把它翻译成原生布局计算,跨端一致。RN 不支持 display: grid(虽然 Yoga 在实验性支持),实战中 Flexbox 已经够用。

  • TextInput 的受控和非受控写法? 受控:value + onChangeText 同步 state,是 RN 的标准做法;非控:只给 defaultValue,之后不再管理。RN 里非受控有已知 bug(如中文输入),强烈建议用受控。

自测三问

  1. RN 中文字必须放在哪个组件里?View 里直接写文字会怎样?
  2. 网络图片和本地图片的引入方式分别是什么?网络图片有什么必须做的事?
  3. 为什么实际项目用 Pressable 而不是 Button?Pressable 的 style 如何响应按压状态?

小结一句话

核心组件就那几个,但记住三条铁律——文字进 Text、图片给宽高、容器用 flex: 1——你就能搭出任何静态界面。

延伸

  • React Native 核心组件官方文档
  • Pressable API 文档
  • 下一课:Flexbox 布局与 FlatList 长列表。
加载本节练习…
← 上一课RN 与 Expo下一课 →布局与列表