本节摘要:React Native 用一套核心内置组件描述界面,用 StyleSheet 定义样式,用 Flexbox 完成布局。本节讲清 View、Text、Image、TextInput、Button 等核心组件的用途与写法,解释样式系统"像 CSS 但不是 CSS"的差异(驼峰命名、对象写法、无继承),并给出 Flexbox 布局的实用要点与常见布局模式。
阅读完本节,你应当能够:
从 Web 转过来的开发者,打开 RN 代码的第一个反应往往是"这些标签我都认识,但为什么没有 div?"。答案是:React Native 的原生世界里没有 div 和 span,只有 View 和 Text。这套命名不是随手起的——View 是"容器",Text 是"文本",名字直接告诉你它画的是什么原生控件。
第二反应通常是"样式怎么不写在 CSS 文件里?"。RN 的样式写在 JavaScript 对象里,通过 StyleSheet.create 组织。这背后是同一个设计逻辑:RN 里一切都是 JavaScript。标签是组件,样式是对象,属性是对象字段——没有独立于 JS 的模板语言和样式语言。这对前端开发者是福音(无需学新语言),但也要接受一个事实:CSS 的很多特性在 RN 里不存在或写法不同。
这套"一切皆 JS"的设计还有一个容易被低估的好处:样式可以参与 JavaScript 逻辑。你可以在渲染时根据状态计算样式(比如选中项变红),可以写函数复用样式组合,可以做响应式判断。样式不再是死板的选择器表,而是活的计算结果。当然代价也随之而来——学习时要把"CSS 心智"替换成"对象心智",这个替换发生在你读完本节之后。

这张图把 RN 的核心组件按用途分成了容器、内容、交互三类。记住一句话就能把握全局:View 管结构、Text 管文字、Image 管图片、交互组件管输入与点击。
View 是最基础的容器,相当于 div,几乎所有组件都嵌套在 View 里。它支持 Flexbox 布局与各种样式。
Text 显示文本,所有文本必须包在 Text 里,支持嵌套对部分文本应用不同样式。
Image 显示图片。网络图片用 source 属性加 uri,本地图片用 require 导入。
TextInput 接收用户输入,是受控组件——value 绑定状态、onChangeText 更新状态。
Button 触发操作,title 是按钮文字,onPress 是点击回调。它在 iOS 与 Android 上是系统样式的按钮。
把每个组件的写法落在代码上,比只记概念更扎实:
import React from 'react'; import { View, Text, Image, TextInput, Button, StyleSheet } from 'react-native'; function ProfileCard() { const [name, setName] = React.useState(''); return ( <View style={styles.card}> <Image source={{ uri: 'https://example.com/avatar.png' }} style={styles.avatar} /> <Text style={styles.title}>个人卡片</Text> <TextInput style={styles.input} placeholder="输入昵称" value={name} onChangeText={setName} /> <Button title="保存" onPress={() => console.log(name)} /> </View> ); } const styles = StyleSheet.create({ card: { padding: 16, backgroundColor: '#fff', borderRadius: 8 }, avatar: { width: 64, height: 64, borderRadius: 32 }, title: { fontSize: 18, fontWeight: 'bold', marginVertical: 8 }, input: { borderWidth: 1, borderColor: '#ccc', padding: 8, marginBottom: 8 }, });
注意几个细节:Image 的网络地址放在 uri 里、本地图片用 require;TextInput 的 value 与 onChangeText 构成受控组件的闭环;Button 的 onPress 传的是函数引用。这个组件把本节的核心组件全部串了起来,能读懂它,核心组件的用法就过了大半。
RN 的样式用 StyleSheet.create 定义,是 JavaScript 对象。它与 CSS 有三个关键差异:
驼峰命名。background-color 变成 backgroundColor,font-size 变成 fontSize。所有连字符属性名都要转成驼峰。
对象写法。样式是对象,不是选择器。不存在"类名选择器",只有"给组件传 style 对象"。想复用样式,就定义一个对象引用它。
无样式继承。Text 的样式不会自动继承给子 Text,每个组件都要显式设置。这与 CSS 的继承机制完全不同,是从 Web 转来的人最容易犯的错。
const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, title: { fontSize: 24, fontWeight: 'bold', color: 'navy' }, });
StyleSheet.create 的好处是集中管理、类型检查、性能优化(同一对象只创建一次)。
RN 默认用 Flexbox 布局,几个核心属性掌握后足以应付绝大多数场景:
flexDirection 决定主轴方向(row 水平、column 垂直),默认是 column。
justifyContent 决定子元素在主轴上如何排列(center 居中、space-between 两端对齐)。
alignItems 决定子元素在交叉轴上如何对齐(center 居中、flex-start 靠起)。
flex 决定子元素在主轴上的弹性伸缩比例。flex: 1 占满剩余空间,两个 flex: 1 各占一半。
一句口诀帮你记住三个属性:方向问 flexDirection,排列问 justifyContent,对齐问 alignItems。
三个属性组合起来能实现常见布局,用一张表把"目标布局 → 属性组合"对应起来,实战时直接查:
| 目标布局 | flexDirection | justifyContent | alignItems |
|---|---|---|---|
| 垂直居中 | column | center | center |
| 水平居左 | row | flex-start | center |
| 两端对齐 | row | space-between | center |
| 底部居中 | column | flex-end | center |
| 等分三列 | row | space-around | center |
这套组合拳几乎覆盖日常所有布局需求。重点理解两点:主轴与交叉轴的概念(方向由 flexDirection 决定)、弹性分配(flex 让子元素按比例瓜分空间)。把这两点想透,布局就是"选方向、定排列、设比例"的流程,不再需要背代码模板。
| 需求 | 用哪个 | 关键点 |
|---|---|---|
| 容器/布局 | View | 最基础的容器 |
| 文本 | Text | 所有文本必包 |
| 图片 | Image | 网络或本地 |
| 用户输入 | TextInput | 受控组件 |
| 点击操作 | Button / Pressable | 按钮或可点击区域 |
| 长列表 | FlatList | 高性能虚拟渲染 |
| 少量滚动内容 | ScrollView | 一次性渲染 |
坑一:忘写 flex: 1 导致空白。父容器没占满屏幕时,子元素布局会"飘"。多数布局问题先检查有没有正确设置 flex。
坑二:Text 里直接写文本没包 Text。RN 里裸文本会报错,一切文本都要在 Text 组件内。
坑三:样式属性名用 CSS 写法。background-color、font-size 这些 CSS 命名在 RN 里不生效,必须用驼峰。
⚠️ 常见坑:把文本直接写在 View 里而不是 Text 里。RN 会报"文本字符串必须在 Text 组件中"的错误,这是新手最高频报错之一。
💡 关键直觉:把 RN 样式想成"带 CSS 风格命名的对象字面量"——不是 CSS 文件,不是 Sass,就是普通 JavaScript 对象。想通这一点,几乎所有样式困惑都会消失。
在 App.js 里写一个包含 View、Text、Image、Button 的小页面,用 StyleSheet 与 Flexbox 完成居中布局,跑起来看效果。重点尝试:改 flexDirection 看排列方向变化,改 alignItems 看对齐变化。这个最小页面就是你后续所有界面开发的起点。
搭完最小页面后,别停在原地,按三步把它扩展成真实页面:
第一步,拆组件。把页面拆成"头部、内容区、底部操作区"几个子组件,各自独立成文件。第 3 章讲过模块化,这里真正用起来。
第二步,接数据。把写死的文本换成从状态或外部传入的数据,让界面"活"起来。这一步会用到第 5 章的状态管理。
第三步,列表化。把一组内容渲染成 FlatList,处理空数据、加载中等状态。到这一步,你已经从一个静态页面进化到可交互的完整页面了。
这三步是 RN 界面开发的通用路径,也是从"能写组件"到"能写页面"的关键跨越。本章后半部分的 4.3 节会带你把这三步走一遍。
问:为什么 TextInput 一定要用受控组件? 受控意味着"界面上显示的始终来自状态",符合声明式思想——界面的唯一来源是状态,输入事件只是更新状态。非受控写法(让输入框自己存值)会在状态不同步时制造诡异 bug。坚持受控,是 RN 开发的默认姿势。
问:FlatList 和 ScrollView 有什么区别? ScrollView 一次性渲染所有子项,内容少时简单直接;FlatList 采用虚拟渲染,只渲染可视区域附近的项,数据量大时性能天差地别。选择标准就一句话:列表数据可能很多就用 FlatList,否则 ScrollView。
问:样式里的颜色、尺寸单位怎么记? 颜色支持十六进制(#fff)与命名颜色(red、navy);尺寸默认是逻辑像素(dp),不是物理像素,系统会自动按屏幕密度缩放。记住"颜色用十六进制、尺寸是逻辑像素",日常开发基本够用。
组件基础掌握后,进阶的方向是"封装可复用组件"——把反复出现的界面模式(比如"带头像的列表项""带图标的按钮")抽成独立组件。判断是否值得封装的标准很简单:同一段 JSX 出现两次以上。封装时给组件设计好 props 接口(外部传入什么、内部管理什么),这决定了它的可复用性。第 4.3 节的实战里会自然遇到封装需求,先记住"重复两次就封装"这个门槛。
把界面代码的组织方式想成三层:组件层(管结构)、样式层(管外观)、数据层(管内容)。写组件时时刻保持三层分离:结构写在 JSX 里、样式写在 StyleSheet 里、内容来自 props 或状态。新手常见的混乱是把样式内联在 JSX 里(结构外观混在一起),或把数据逻辑写进样式函数。保持三层分工,组件可读性与可维护性都会明显提升,这也是从"能写"到"会写"的重要分界。
StyleSheet.create 除了让样式集中、可读性好,还有一个性能价值:它在内部对样式对象做了一次性登记,重复引用同一样式不会重复创建。对比直接在 JSX 里写内联对象 style={{ padding: 20 }},后者每次渲染都会创建新对象,引发不必要的比较。所以实践规则是:能进 StyleSheet 的样式都进 StyleSheet,内联对象只用于确实需要动态计算的值(如根据状态改变颜色)。这条规则简单,但长期看对列表这类高频渲染场景的性能有实打实的影响。
RN 的 style 属性还支持数组写法,按顺序合并多个样式对象:style={[styles.base, isActive && styles.active]}。这个技巧在"条件样式"场景下非常实用——基础样式固定,状态样式按条件叠加。它也是动态样式的标准写法,比写三元表达式返回完整对象更清晰。掌握这个写法,你在"列表项选中态""输入框聚焦态"这类交互样式里会写得更顺手。
样式的学习可以分三层来定位自己的进度。第一层是"会用"——知道 StyleSheet 怎么写、flex 怎么配,能把界面摆出来;第二层是"会调"——遇到布局不对能通过 Inspector 或 Flexbox 三个属性定位问题;第三层是"会设计"——能主动规划组件的样式接口,让样式可复用、可扩展。多数入门者停在第一层,本节的补充内容是在帮你往第二、三层走。别着急,样式能力是在一次次"改到对"的过程中积累的,没有捷径但有方法。
RN 的组件与样式基础已建立,下一节看 Flutter 的布局 Widget——用另一种语法解决同样的布局问题。