组件与 JSX

要点

JSX 表达组件树,View 组织布局,Text 显示文字。原生布局中的尺寸、主轴和交叉轴需要结合父容器理解。

核心组件速览

组件类比 Web用途
Viewdiv布局容器,支持 Flexbox
Textp/span唯一能渲染文本的组件
Imageimg网络图/本地资源/URI 图片
ScrollViewoverflow 滚动区全量渲染内容的滚动容器
FlatList无直接对应虚拟化长列表,按需渲染

核心规则: RN 中文本必须包在 <Text> 里,<View>hello</View> 会直接报错。

样式与 Flexbox

RN 样式是 CSS 的子集,没有单位(数字 = 密度无关像素 dp),不支持伪类与选择器,属性名一律驼峰。

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

function LayoutDemo() {
  return (
    // 默认 flexDirection 是 'column'(Web 默认 row),这是新手第一大坑
    <View style={styles.page}>
      <View style={styles.header}>
        <Text style={styles.headerText}>顶部栏</Text>
      </View>
      <View style={styles.body}>
        <View style={styles.card}>
          <Text>卡片一:flex:1 平分剩余空间</Text>
        </View>
        <View style={styles.card}>
          <Text>卡片二</Text>
        </View>
      </View>
      <View style={styles.footer}>
        <Text>底部固定栏</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  page: { flex: 1 },
  header: { height: 56, backgroundColor: '#4A90D9', justifyContent: 'center', alignItems: 'center' },
  headerText: { color: '#fff', fontSize: 18 },
  body: { flex: 1, flexDirection: 'row', padding: 12, gap: 12 },
  card: { flex: 1, backgroundColor: '#f0f2f5', borderRadius: 8, padding: 16 },
  footer: { height: 64, backgroundColor: '#eee', justifyContent: 'center', alignItems: 'center' },
});

Web ↔ RN 差异对照:

  • flexDirection 默认 column(Web 默认 row)
  • 主轴为 column 时,alignItems/justifyContent 的作用方向与 Web 直觉相反
  • 无 position: static;默认 relative,absolute 相对父容器定位
  • 支持简写 flex: 1(等价 flexGrow: 1, flexShrink: 1, flexBasis: 0%)
  • 尺寸无 px/rem,纯数字按 dp/pt 处理;可用 PixelRatio 换算

Image 的三种来源

import { Image } from 'react-native';

// 1. 本地静态资源(构建期打包,必须用 require 且路径为字面量)
const imageExamples = <>
<Image source={require('./assets/logo.png')} style={{ width: 40, height: 40 }} />

// 2. 网络图片(必须显式声明宽高)
<Image
  source={{ uri: 'https://example.com/avatar.png' }}
  style={{ width: 48, height: 48, borderRadius: 24 }}
/>

// 3. base64 / data URI
<Image source={{ uri: 'data:image/png;base64,iVBORw0KG...' }} style={{ width: 24, height: 24 }} />
</>;

ScrollView vs FlatList

ScrollView: 一次性渲染全部子元素。适合表单、设置页等确定数量的内容。

FlatList: 只渲染可视区域附近的条目。数据量未知、可能持续增长,或行组件成本较高时优先考虑它;不存在适用于所有设备的“超过 20 条就必须换”的阈值。

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

interface Todo {
  id: string;
  title: string;
  done: boolean;
}

const TODOS: Todo[] = Array.from({ length: 200 }, (_, i) => ({
  id: `${i}`,
  title: `任务 ${i + 1}`,
  done: i % 3 === 0,
}));

const ROW_HEIGHT = 57; // 本示例:56 高的行 + 1 高的分隔线

function TodoList() {
  return (
    <FlatList
      data={TODOS}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <View style={{ height: ROW_HEIGHT - 1, justifyContent: 'center', paddingHorizontal: 16 }}>
          <Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>
            {item.title}
          </Text>
        </View>
      )}
      // 性能相关 props 的完整清单见 reference 组件 Props 全表
      initialNumToRender={10}
      getItemLayout={(_data, index) => ({
        length: ROW_HEIGHT, // 只在“行高与分隔线总高度真的固定”时使用
        offset: ROW_HEIGHT * index,
        index,
      })}
      ListEmptyComponent={<Text>暂无待办</Text>}
      ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: '#eee' }} />}
    />
  );
}

FlatList 性能 props(windowSize、removeClippedSubviews 等)的完整参数表见 组件 Props 全表。

最佳实践

列表中的 key 用来识别项目身份,插入或排序后仍应对应同一条数据;用数组下标可能让输入状态跟错行。大量内容用虚拟列表减少同时挂载的项目,并通过滚动检查空白、复用和触控行为。

StyleSheet 有助于组织与检查样式,动态样式对象也合法;是否值得稳定引用要看实际渲染成本。网络图需要可确定的布局尺寸,测量布局用 onLayout,不靠猜测延时。长列表的页头可交给列表自身,避免外层同向滚动容器削弱虚拟化。

常见问题

Q1: 为什么我的元素不显示?

A: 三查——父容器是否有 flex: 1 撑开、元素是否有显式宽高或 flex、Text 是否存在。RN 没有 Web 的”内容自动撑高”兜底那么宽松。

Q2: 阴影样式在 Android 上不生效?

A: 先检查所用 RN 版本及是否采用新架构:较旧 Android 实现常用 elevation,而 shadowColor、shadowOffset、shadowOpacity、shadowRadius 的跨平台支持随版本变化。为目标版本分别在 Android/iOS 设备确认,不要只在一端看到阴影就假定另一端相同。

Q3: FlatList 滑动掉帧怎么办?

A: getItemLayout 跳过测量、renderItem 里的组件用 React.memo、图片用固定宽高,进阶优化见 07-advanced-features。

练习与实践

练习一:个人资料卡

任务要求: 1. 用 View/Image/Text 实现一张资料卡:头像(网络图)、姓名、简介、三个横排统计数字 2. 头像圆形,卡片圆角带 elevation 阴影

评估标准: 在已选目标平台确认头像不变形、长简介会换行且统计数字不溢出;第二平台可作为后续差异检查,记录实际差异而非预设“必然一致”。

练习二:长列表性能对比

任务要求: 1. 分别用 ScrollView 和 FlatList 渲染 500 条数据,给每一行加入稳定 id。 2. 在同一台目标设备上快速滚动、跳转到中部,再删掉一项;观察初次打开时间、滚动是否空白和删除后内容是否错行。

提示: ScrollView 是否卡顿取决于设备和行组件复杂度,不能把某个数量当作必然 OOM 的证明。验收重点是:长列表需要按需渲染时,FlatList 的稳定 key、行高度假设和滚动行为都可解释。


相关文档

可复跑:JSX 子节点的结构契约

React Native 的 <Text>、<View> 等最终是 React 元素。JSX 中嵌套数组与条件子节点会先经过 React 的子节点规则;列表项必须提供稳定 key,不能把“数组能渲染”误解成“下标 key 在插入后仍安全”。下面运行真实 React 的 Children.toArray,只验证 JSX/React 的元素结构,不声称验证原生 Text 渲染或 Flexbox。

import React from 'react'

const rows = [{ id: 'a', title: '第一项' }, { id: 'b', title: '第二项' }]
const children = rows.map((row) => React.createElement('Text', { key: row.id }, row.title))
const flattened = React.Children.toArray([null, children, false])

if (flattened.length !== 2) throw new Error('空条件节点不应成为可渲染条目')
if (flattened.map((child) => child.props.children).join(',') !== '第一项,第二项') {
  throw new Error('JSX 子节点顺序错误')
}
if (!flattened.every((child) => child.key !== null)) throw new Error('列表项缺少稳定 key')
console.log('React Native JSX children contracts passed')

要验证原生规则,请继续在 Android 与 iOS 模拟器或真机运行:View 下的裸文本报错、长文本换行、无障碍树和触摸区域都属于宿主平台行为,不由这段 Node 程序覆盖。

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇