RN核心语法与组件体系:UI布局与基础交互
在上一篇文章中,我们完成了RN开发环境的搭建并实现了第一个HelloWorld应用。本文作为RN体系化专栏的第二篇,将聚焦RN核心组件、Flex布局体系、样式系统与基础交互,帮助你从"能运行"进阶到"能独立搭建页面UI并实现用户交互",为后续复杂应用开发夯实基础。
一、RN核心基础组件:构建UI的基石
RN提供了一套封装好的基础组件,这些组件最终会映射为对应平台的原生控件,既保证了跨端一致性,又具备原生级的交互体验。我们先从最常用的核心组件入手,逐个拆解其用法。
1. 容器与文本:View与Text
(1)View组件(基础容器)
View是RN中最基础的容器组件,类似于Web中的div,用于承载其他组件、实现布局嵌套。它支持Flex布局、样式设置和触摸事件,是页面结构的核心骨架。
基础用法:
jsx
import { View, StyleSheet } from 'react-native';
export default function BasicView() {
return (
// 外层容器,采用Flex布局居中内部元素
<View style={styles.container}>
{/* 内层子容器,设置背景色和宽高 */}
<View style={styles.box1} />
<View style={styles.box2} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, // 占满父容器剩余空间
backgroundColor: '#f5f5f5',
justifyContent: 'center', // 主轴(垂直)居中
alignItems: 'center', // 交叉轴(水平)居中
},
box1: {
width: 100,
height: 100,
backgroundColor: '#0066cc',
marginBottom: 20, // 与下方box2的间距
},
box2: {
width: 80,
height: 80,
backgroundColor: '#ff6600',
borderRadius: 10, // 圆角效果
},
});
(2)Text组件(文本展示)
Text是RN中唯一能显示文本的组件(Web中任意标签可写文本,但RN不行),支持字体样式、换行、富文本和文本事件。
基础用法:
jsx
import { Text, View, StyleSheet } from 'react-native';
export default function BasicText() {
return (
<View style={styles.container}>
{/* 基础文本 */}
<Text style={styles.title}>React Native 组件学习</Text>
{/* 多行文本与样式嵌套(富文本) */}
<Text style={styles.content}>
这是一段多行文本,支持
<Text style={styles.highlight}>样式嵌套</Text>
,还能实现
<Text style={styles.link}>点击跳转</Text>
等交互。
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#fff',
},
title: {
fontSize: 22,
fontWeight: 'bold', // 加粗
color: '#333',
marginBottom: 15,
},
content: {
fontSize: 16,
color: '#666',
lineHeight: 24, // 行高,控制文本间距
},
highlight: {
color: '#0066cc',
fontWeight: '600',
},
link: {
color: '#ff6600',
textDecorationLine: 'underline', // 下划线,模拟链接
},
});
2. 图片展示:Image组件
Image组件用于加载本地或网络图片,支持缓存、缩放、圆角等功能,是实现图文页面的核心组件。
(1)加载网络图片
jsx
import { Image, View, StyleSheet } from 'react-native';
export default function NetworkImage() {
return (
<View style={styles.container}>
<Image
style={styles.image}
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }} // 网络图片URI
resizeMode="contain" // 缩放模式:保持宽高比,完整显示图片
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
image: {
width: 200,
height: 200,
borderRadius: 10,
},
});
(2)加载本地图片
首先将图片放入项目assets/images目录(需自行创建),然后通过require引入:
jsx
// 本地图片需指定相对路径
<Image
style={styles.localImage}
source={require('../assets/images/avatar.png')}
/>
// 对应的样式
localImage: {
width: 80,
height: 80,
borderRadius: 40, // 圆形头像
},
关键属性说明:
resizeMode:控制图片缩放方式,常用值有contain(保持比例)、cover(铺满容器,裁剪多余部分)、stretch(拉伸填充,可能变形);fadeDuration:设置图片加载时的淡入动画时长(仅iOS);cachePolicy:网络图片缓存策略,如cacheOnly(仅从缓存加载)。
3. 交互组件:Touchable系列与TextInput
RN的交互组件分为两类:触摸反馈组件 (实现点击/长按等操作)和输入组件(实现用户输入)。
(1)Touchable系列组件
RN没有Web中的button标签,而是通过Touchable系列组件实现触摸交互,常用的有TouchableOpacity(点击时透明度变化)、TouchableHighlight(点击时背景高亮)、TouchableWithoutFeedback(无视觉反馈)。
最常用的TouchableOpacity:
jsx
import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native';
export default function TouchableDemo() {
const handlePress = () => {
// 弹出提示框
Alert.alert('提示', '按钮被点击了!');
};
const handleLongPress = () => {
Alert.alert('提示', '按钮被长按了!');
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={handlePress} // 点击事件
onLongPress={handleLongPress} // 长按事件
activeOpacity={0.7} // 点击时透明度(默认0.2)
>
<Text style={styles.buttonText}>点击我</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
backgroundColor: '#0066cc',
paddingHorizontal: 30,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '500',
},
});
(2)TextInput(文本输入)
TextInput用于实现用户输入,支持单行/多行输入、占位符、输入限制、密码隐藏等功能,是表单页面的核心组件。
基础用法:
jsx
import { View, TextInput, StyleSheet, Text } from 'react-native';
import { useState } from 'react';
export default function TextInputDemo() {
// 用useState管理输入框内容
const [inputValue, setInputValue] = useState('');
return (
<View style={styles.container}>
<Text style={styles.label}>请输入用户名:</Text>
<TextInput
style={styles.input}
placeholder="请输入您的用户名" // 占位提示文字
placeholderTextColor="#999" // 占位文字颜色
value={inputValue} // 绑定输入值
onChangeText={(text) => setInputValue(text)} // 输入变化时更新状态
maxLength={20} // 最大输入长度
clearButtonMode="while-editing" // iOS专属:编辑时显示清除按钮
autoCapitalize="none" // 关闭自动大写
/>
{/* 实时显示输入内容 */}
<Text style={styles.tip}>已输入:{inputValue}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#fff',
},
label: {
fontSize: 16,
color: '#333',
marginBottom: 8,
},
input: {
height: 44,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
paddingHorizontal: 12,
fontSize: 16,
marginBottom: 15,
},
tip: {
fontSize: 14,
color: '#666',
},
});
4. 列表组件:FlatList(高效渲染长列表)
当需要展示大量数据列表时,直接用View嵌套多个组件会导致性能卡顿,RN提供了FlatList组件,通过懒加载 和复用组件实现高效渲染,是长列表的首选方案。
基础用法:
jsx
import { FlatList, View, Text, StyleSheet, Image } from 'react-native';
// 模拟列表数据
const DATA = [
{ id: '1', title: 'RN基础组件', author: '前端小白', avatar: 'https://placehold.co/40x40' },
{ id: '2', title: 'Flex布局实战', author: 'RN进阶者', avatar: 'https://placehold.co/40x40' },
{ id: '3', title: '状态管理入门', author: '全栈开发', avatar: 'https://placehold.co/40x40' },
{ id: '4', title: '原生模块交互', author: '资深工程师', avatar: 'https://placehold.co/40x40' },
{ id: '5', title: '性能优化技巧', author: '架构师', avatar: 'https://placehold.co/40x40' },
// 可扩展更多数据
];
// 列表项组件
const ListItem = ({ item }) => (
<View style={styles.itemContainer}>
<Image source={{ uri: item.avatar }} style={styles.avatar} />
<View style={styles.textContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.author}>作者:{item.author}</Text>
</View>
</View>
);
export default function FlatListDemo() {
return (
<FlatList
data={DATA} // 数据源
renderItem={({ item }) => <ListItem item={item} />} // 渲染列表项
keyExtractor={(item) => item.id} // 列表项唯一标识(必须)
style={styles.list}
ItemSeparatorComponent={() => <View style={styles.separator} />} // 列表项分隔线
ListHeaderComponent={() => <Text style={styles.header}>RN学习资源列表</Text>} // 列表头部
ListEmptyComponent={() => <Text style={styles.empty}>暂无数据</Text>} // 数据为空时显示
/>
);
}
const styles = StyleSheet.create({
list: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
fontSize: 18,
fontWeight: 'bold',
padding: 15,
backgroundColor: '#fff',
},
itemContainer: {
flexDirection: 'row', // 横向排列
alignItems: 'center',
padding: 15,
backgroundColor: '#fff',
},
avatar: {
width: 40,
height: 40,
borderRadius: 20,
marginRight: 12,
},
textContainer: {
flex: 1, // 占满剩余空间
},
title: {
fontSize: 16,
color: '#333',
marginBottom: 4,
},
author: {
fontSize: 14,
color: '#999',
},
separator: {
height: 1,
backgroundColor: '#eee',
},
empty: {
textAlign: 'center',
padding: 20,
color: '#999',
},
});
核心属性说明:
data:列表数据源,必须是数组;renderItem:渲染单个列表项的函数;keyExtractor:为每个列表项生成唯一key,避免渲染异常;ItemSeparatorComponent:列表项之间的分隔线;ListHeaderComponent/ListFooterComponent:列表头部/尾部组件;onEndReached:列表滚动到底部时触发,用于实现下拉加载更多。
二、RN Flex布局体系:搞定跨端页面布局
RN默认采用Flex布局(弹性布局),不同于Web中Flex需手动开启,RN的View组件天然支持Flex,通过简单的属性即可实现复杂的页面排版,是跨端布局的核心方案。
1. Flex布局核心概念
Flex布局包含容器 和项目两个角色:
- 容器 :开启Flex的父组件(RN中
View默认是Flex容器); - 项目:容器内的子组件。
Flex布局有两根轴:
- 主轴 :默认是垂直方向(
flexDirection: column),可改为水平方向(row); - 交叉轴:与主轴垂直的轴,主轴为垂直时交叉轴为水平,反之亦然。
2. 容器常用属性
(1)flexDirection:控制主轴方向
| 属性值 | 主轴方向 | 适用场景 |
|---|---|---|
column(默认) |
垂直方向,从上到下 | 移动端页面的纵向排版(如列表、表单) |
row |
水平方向,从左到右 | 横向排列的组件(如导航栏、标签栏) |
column-reverse |
垂直方向,从下到上 | 反向纵向排列 |
row-reverse |
水平方向,从右到左 | 反向横向排列 |
示例:横向排列导航栏
jsx
<View style={{ flexDirection: 'row', justifyContent: 'space-around', padding: 10 }}>
<Text style={styles.navItem}>首页</Text>
<Text style={styles.navItem}>课程</Text>
<Text style={styles.navItem}>我的</Text>
</View>
(2)justifyContent:控制项目在主轴上的对齐方式
| 属性值 | 对齐效果 |
|---|---|
flex-start(默认) |
靠近主轴起点 |
flex-end |
靠近主轴终点 |
center |
主轴居中 |
space-between |
项目之间间距相等,两端对齐 |
space-around |
项目两侧间距相等 |
space-evenly |
所有间距(包括两端)相等 |
(3)alignItems:控制项目在交叉轴上的对齐方式
| 属性值 | 对齐效果 |
|---|---|
stretch(默认) |
拉伸填满交叉轴(需项目无固定尺寸) |
flex-start |
靠近交叉轴起点 |
flex-end |
靠近交叉轴终点 |
center |
交叉轴居中 |
baseline |
按项目基线对齐(适用于文本) |
(4)flexWrap:控制项目是否换行
当项目总尺寸超过容器时,通过flexWrap控制是否换行,默认nowrap(不换行,项目会被压缩)。
wrap:自动换行;nowrap:不换行(默认)。
3. 项目常用属性
flex:控制项目在主轴上的占比,数值为权重(如两个项目flex:1和flex:2,则分别占1/3和2/3空间);alignSelf:覆盖容器的alignItems,单独设置某个项目的交叉轴对齐方式;margin/padding:控制项目的内外边距(与Web一致)。
4. Flex布局实战:登录页面
jsx
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Image } from 'react-native';
import { useState } from 'react';
export default function LoginPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = () => {
console.log('登录信息:', { username, password });
};
return (
<View style={styles.container}>
{/* 顶部logo */}
<Image
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
style={styles.logo}
/>
{/* 表单区域 */}
<View style={styles.formContainer}>
<TextInput
style={styles.input}
placeholder="请输入用户名"
value={username}
onChangeText={setUsername}
/>
<TextInput
style={styles.input}
placeholder="请输入密码"
value={password}
onChangeText={setPassword}
secureTextEntry={true} // 密码隐藏
/>
{/* 登录按钮 */}
<TouchableOpacity style={styles.loginBtn} onPress={handleLogin}>
<Text style={styles.btnText}>登录</Text>
</TouchableOpacity>
{/* 辅助链接 */}
<View style={styles.linkContainer}>
<Text style={styles.link}>忘记密码?</Text>
<Text style={styles.link}>注册账号</Text>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
alignItems: 'center', // 交叉轴水平居中
paddingTop: 60,
},
logo: {
width: 80,
height: 80,
marginBottom: 40,
},
formContainer: {
width: '85%', // 宽度占父容器85%
},
input: {
height: 48,
backgroundColor: '#fff',
borderRadius: 8,
paddingHorizontal: 15,
fontSize: 16,
marginBottom: 12,
},
loginBtn: {
backgroundColor: '#0066cc',
borderRadius: 8,
paddingVertical: 14,
alignItems: 'center',
marginVertical: 15,
},
btnText: {
color: '#fff',
fontSize: 16,
fontWeight: '500',
},
linkContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
link: {
color: '#0066cc',
fontSize: 14,
},
});
三、RN样式系统:StyleSheet与样式适配
RN的样式系统类似CSS,但有明显差异:
- 样式属性采用驼峰命名 (如
backgroundColor而非background-color); - 没有CSS选择器,样式需通过
style属性直接绑定; - 推荐使用
StyleSheet.create创建样式,可提前校验样式合法性、优化性能。
1. StyleSheet核心用法
StyleSheet.create用于创建样式对象,支持样式复用和嵌套(有限),是RN样式开发的标准方式。
jsx
import { StyleSheet } from 'react-native';
// 全局样式常量(可抽离到单独文件)
export const globalStyles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
text: {
fontSize: 16,
color: '#333',
},
// 样式嵌套(仅支持简单嵌套,复杂样式需拆分为多个属性)
button: {
padding: 12,
borderRadius: 8,
backgroundColor: '#0066cc',
// 无法直接嵌套hover样式,RN需通过state控制交互样式
},
});
2. 样式优先级
RN样式优先级从高到低为:内联样式 > StyleSheet样式 > 全局样式。
jsx
// 内联样式会覆盖StyleSheet中的color属性
<Text style={[globalStyles.text, { color: '#ff6600' }]}>优先级测试</Text>
3. 尺寸适配:应对不同屏幕
RN中尺寸单位为逻辑像素(dp),会自动根据设备像素密度(DPI)转换为物理像素,无需手动处理,但需注意屏幕宽度适配:
- 推荐使用百分比 或Flex权重 适配宽度(如
width: '80%'); - 可通过
DimensionsAPI获取屏幕尺寸,实现精准适配。
示例:获取屏幕尺寸
jsx
import { Dimensions } from 'react-native';
// 获取屏幕宽高
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
// 适配不同屏幕的图片宽度
const imageWidth = screenWidth * 0.8; // 占屏幕宽度80%
四、基础交互与简单动画
除了点击、输入等基础交互,RN还提供了AnimatedAPI实现简单的动画效果,提升页面体验。
1. 触摸事件进阶:传递参数
实际开发中,列表项点击常需传递数据(如ID),可通过箭头函数实现:
jsx
<TouchableOpacity onPress={() => handleItemClick(item.id)}>
<Text>{item.title}</Text>
</TouchableOpacity>
2. 简单动画:Animated实现渐隐渐现
jsx
import { View, Animated, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useState, useRef } from 'react';
export default function SimpleAnimation() {
// 创建动画值,初始为0(透明)
const fadeAnim = useRef(new Animated.Value(0)).current;
const [isShow, setIsShow] = useState(false);
const toggleAnimation = () => {
if (isShow) {
// 隐藏动画:从1(不透明)变为0(透明)
Animated.timing(fadeAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true, // 使用原生驱动,提升动画性能
}).start(() => setIsShow(false));
} else {
// 显示动画:从0变为1
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start(() => setIsShow(true));
}
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.btn} onPress={toggleAnimation}>
<Text style={styles.btnText}>{isShow ? '隐藏方块' : '显示方块'}</Text>
</TouchableOpacity>
{/* 动画组件 */}
<Animated.View
style={[
styles.box,
{
opacity: fadeAnim, // 绑定透明度动画
transform: [{ scale: fadeAnim }], // 同时实现缩放动画
},
]}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
btn: {
backgroundColor: '#0066cc',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
marginBottom: 30,
},
btnText: {
color: '#fff',
fontSize: 16,
},
box: {
width: 100,
height: 100,
backgroundColor: '#ff6600',
borderRadius: 10,
},
});
五、小结与下一阶段预告
本文系统讲解了RN的核心基础组件、Flex布局体系、样式系统和基础交互,你已具备独立搭建常规页面UI和实现简单交互的能力。通过实战案例(登录页、列表页),你也能感受到RN跨端布局的便捷性和组件化开发的优势。
下一篇文章《RN路由与状态管理:打造多页面应用》,将带你学习React Navigation路由配置和Redux状态管理,解决多页面跳转和数据共享问题,实现从"单页面"到"多页面应用"的跨越。
如果你在组件使用或布局调试中遇到问题,可随时留言,我会为你提供针对性的解决方案!