Published on

React Native 基础组件与样式化

React Native (RN) 作为一个使用 React 构建原生移动应用的框架,虽然共享了 React 的组件化、声明式 UI 等核心思想,但其渲染目标是移动设备的原生 UI 控件,而非浏览器的 DOM。这一根本性的差异,决定了 RN 在基础组件的使用、样式化方法以及布局模型上,与 Web 开发存在着显著的不同。

核心基础组件

在 RN 中,所有 UI 都是由原生组件构成的。其中,ViewText 是构建任何界面的基础。

  • <View>: 这是构建 UI 时最基础的容器组件。它支持布局、样式化和触摸事件处理,其角色类似于 Web 开发中的 <div> 标签。
  • <Text>: 这是一个用于显示文本的专用组件。
文本渲染的硬性规则

在 React Native 中,所有需要被渲染的文本内容,都必须被包裹在 <Text> 组件之内。将裸文本直接放置在 <View> 或其他组件中,会导致程序抛出错误。这是 RN 与 Web (HTML) 的一个核心区别。

React Native 中的样式化

RN 的样式化系统在概念上与 CSS 相似,但在语法和实现上存在关键差异。

样式定义:JavaScript 对象

所有样式都通过 JavaScript 对象来定义,而非独立的 .css 文件。样式属性的命名遵循驼峰式命名法 (camelCase),例如 backgroundColor 对应 Web CSS 中的 background-color

无单位尺寸系统:密度无关像素

在 RN 中,所有的尺寸(如 width, height, margin)都使用无单位的数字。这个数字代表的是密度无关像素 (Density-Independent Pixels, DP)

密度无关像素 (DP)

DP 是一种抽象的单位,它能够确保 UI 元素在不同像素密度的设备屏幕上,展现出一致的物理尺寸。RN 会在底层根据设备的像素比率 (PixelRatio),自动将 DP 转换为真实的物理像素 (PX)。

物理像素 = DP 值 × PixelRatio

样式应用方式

  • 内联样式 (Inline Styles): 可以直接将样式对象传递给组件的 style 属性,适用于简单的、一次性的样式。
<Text style={{ color: 'blue', fontSize: 16 }}>Hello, World!</Text>
  • StyleSheet.create (最佳实践): StyleSheet.create 方法用于创建集中管理的、经过优化的样式表对象。
StyleSheet.create 的优势

  1. 性能优化: 样式对象在创建后会被赋予一个唯一的 ID,并只通过原生桥 (Bridge) 传递一次。在后续渲染中,只需传递这个 ID 即可,避免了重复创建和传递整个样式对象所带来的开销。
  2. 代码组织: 将样式与组件的渲染逻辑分离,提高了代码的可读性和可维护性。
  3. 静态验证: 可以在开发阶段对样式属性的拼写和值进行验证,提前发现错误。
  • 样式数组 (Style Arrays): style 属性可以接收一个由样式对象组成的数组。数组中靠后的样式会覆盖靠前的样式,这为实现条件样式和动态样式提供了极大的便利。
import { StyleSheet, Text, View } from 'react-native';

// 使用 StyleSheet.create 创建样式表
const styles = StyleSheet.create({
  container: {
    padding: 16,
  },
  text: {
    fontSize: 18,
    color: 'black',
  },
  highlightedText: {
    color: 'tomato',
    fontWeight: 'bold',
  },
});

function MyComponent({ isHighlighted }) {
  return (
    <View style={styles.container}>
      {/* 使用样式数组来应用条件样式 */}
      <Text style={[styles.text, isHighlighted && styles.highlightedText]}>
        This text might be highlighted.
      </Text>
    </View>
  );
}

默认布局模型:Flexbox

React Native 默认使用 Flexbox 进行布局,所有 <View> 元素天生就是一个 Flex 容器。但其默认值与 Web 端的 Flexbox 存在一些关键差异。

React Native Flexbox 与 Web 的核心差异

  • flexDirection: RN 的默认主轴方向是 'column'(从上到下),而 Web 是 'row'(从左到右)。这是最根本的区别,它更符合移动应用从上到下的典型布局模式。
  • flex: flex 属性在 RN 中只接受一个数字,它等同于 Web CSS 中的 flex-growflex-shrink: 1flex-basis: 0% 的组合。flex: 1 是最常用的样式,它意味着元素将占据所有可用的剩余空间。

可交互组件

Button vs. Pressable

  • Button: RN 内置的 <Button> 组件会渲染出对应平台的原生按钮(iOS 和 Android 的外观不同)。它的优点是简单易用,但缺点是可定制性极差,几乎无法修改其样式。
  • TouchableOpacity & Pressable: 为了构建自定义的按钮和交互元素,开发者通常使用这两个组件。
    • TouchableOpacity: 一个简单的封装,当用户触摸时,会将其子组件的透明度 (opacity) 降低,产生一个视觉反馈。可以通过 activeOpacity 属性控制按压时的透明度。
    • Pressable (现代推荐): 这是新一代的、功能更强大的交互封装组件。它提供了对按压状态 (pressed state) 的精细控制,允许你根据不同的交互阶段(如 onPressIn, onPressOut)来动态地改变样式。
使用 Pressable 构建自定义按钮

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

const CustomButton = ({ onPress, title }) => (
  <Pressable
    onPress={onPress}
    // style 属性可以接收一个函数,根据按压状态返回不同样式
    style={({ pressed }) => [
      styles.button,
      {
        backgroundColor: pressed ? 'rgb(210, 230, 255)' : 'dodgerblue',
      },
    ]}>
      <Text style={styles.buttonText}>{title}</Text>
  </Pressable>
)

const styles = StyleSheet.create({
  button: {
    padding: 12,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
});