阅读视图

发现新文章,点击刷新页面。

Expo 进阶指南:赋予 TanStack Query “原生感知力” —— 深度解析 AppState 与 NetInfo

在 Web 开发中,我们习惯了浏览器的“智能”:切换标签页时页面会自动刷新,断网重连后请求会自动重试。这是因为浏览器底层自动处理了窗口聚焦(Window Focus)和网络状态(Online Status)的事件。

然而,在 React Native (Expo) 环境中,App 默认是“盲”和“聋”的。TanStack Query 不知道用户什么时候把 App 切到了后台,也不知道手机什么时候断了网。

为了让 App 拥有极致的用户体验(如:切回来自动刷新、断网自动暂停重试),我们需要手动配置 “感官系统” 。本文将深入解读 AppStateStatussetOnline,并提供生产环境的完整配置方案。


一、 深度解读:App 的“感官系统”

1. 视觉神经:AppStateStatus (应用状态)

AppStateStatus 是 React Native 原生提供的一个状态类型,用于描述 App 当前处于什么处境。

三个核心状态:

  • active (前台/活跃) :用户正在盯着屏幕,App 在最上层运行。这是我们唯一希望触发数据刷新的状态。
  • background (后台) :用户按了 Home 键,或者切换到了微信。App 还在内存中运行,但界面不可见。
  • inactive (非活跃) :App 处于“半死不活”的过渡态(如被来电画面覆盖、拉下了通知栏、iOS 多任务切换界面)。

为什么要配置它?

TanStack Query 有一个核心功能叫 Window Focus Refetching。

  • 问题:RN 默认没有 window.onfocus 事件。
  • 解决:我们需要监听 AppState 的变化。当状态变为 active 时,手动调用 focusManager.setFocused(true)
  • 效果:TanStack Query 收到信号后,会立即检查页面上的数据是否过期(Stale)。如果过期,它会在后台悄悄发起请求,用户切回来的一瞬间看到的就是最新数据。

2. 听觉神经:setOnline (联网回调)

setOnline 不是一个你可以直接导入的函数,它是 TanStack Query 的 onlineManager.setEventListener 方法传递给你的一个回调函数(参数)。你可以把它理解为 TanStack Query 递给你的一把“开关”。

逻辑流程图:从断网到重连

+---------------------------+
| 📡 物理层                  |
| 手机 4G/WiFi 信号变化       |
+-------------+-------------+
              |
              v (触发系统事件)
+-------------+-------------+
| 🎧 B: NetInfo 监听器       |
+-------------+-------------+
              |
              v (获取状态: isConnected)
+-------------+-------------+
| 💻 C: 开发者代码逻辑        |
| (app/_layout.tsx)         |
+-------------+-------------+
              |
              v (调用 setOnline)
+-------------+-------------+
| ⚙️ D: TanStack Query      |
|     (onlineManager)       |
+-------------+-------------+
       /             \
      / (false)       \ (true)
     v                 v
+---------+       +---------+
| E: 🛑   |       | F: 🚀   |
| 暂停     |       | 恢复    |
| (Pause) |       | (Resume)|
| 停止重试 |       | 立即重试 |
+---------+       +---------+

为什么要配置它?

  • 省电保护:如果没配置,断网时 TanStack Query 会傻傻地一直重试请求,消耗用户电量。配置后,断网即休眠。
  • 体验优化:如果没配置,网络恢复后,App 不会有反应,用户必须手动下拉刷新。配置后,信号恢复的瞬间,数据自动加载成功(“电梯效应”)。

二、 生产环境完整配置 (app/_layout.tsx)

这是融合了上述原理的完整配置代码。请确保你已安装了 @react-native-community/netinfo

import { useEffect } from 'react';
import { AppState, AppStateStatus, Platform } from 'react-native';
import { Slot } from 'expo-router';
import NetInfo from '@react-native-community/netinfo';
import { 
  QueryClient, 
  QueryClientProvider, 
  focusManager, 
  onlineManager 
} from '@tanstack/react-query';

// =================================================================
// 1. 配置网络监听 (给 App 装上“耳朵”)
// =================================================================
onlineManager.setEventListener((setOnline) => {
  // setOnline 是 Query 传进来的一个函数,用来接收网络状态
  // 我们使用 NetInfo 来监听真实的网络变化
  return NetInfo.addEventListener((state) => {
    // state.isConnected 可能为 null,用 !! 强转为 boolean
    // 当这里调用 setOnline(true) 时,Query 会立即重试刚才失败的请求
    setOnline(!!state.isConnected);
  });
});

// =================================================================
// 2. 配置 App 状态监听 (给 App 装上“眼睛”)
// =================================================================
function onAppStateChange(status: AppStateStatus) {
  // Web 浏览器会自动处理窗口聚焦,只有原生 App 需要手动处理
  if (Platform.OS !== 'web') {
    // 核心逻辑:
    // 当 AppState 变为 'active' (前台) 时
    // 我们手动告诉 Query 的 focusManager:“用户现在聚焦在 App 上了”
    // Query 收到信号后,会检查页面数据是否过期 (stale),如果过期则自动 Refetch
    focusManager.setFocused(status === 'active');
  }
}

// =================================================================
// 3. 初始化 QueryClient (配置全局策略)
// =================================================================
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 2,             // 失败后自动重试 2 次
      staleTime: 1000 * 60, // 数据 1 分钟内算“新鲜”,切回来也不刷新
                            // 超过 1 分钟后切回来,会触发自动刷新
    },
  },
});

export default function RootLayout() {
  // 注册 AppState 监听器
  useEffect(() => {
    // 开始监听系统状态变化
    const subscription = AppState.addEventListener('change', onAppStateChange);
    
    // 组件卸载时取消监听,这是防止内存泄漏的标准做法
    return () => subscription.remove();
  }, []);

  return (
    
      {/* Slot 是 Expo Router 的页面入口 */}
      
    
  );
}

三、 总结

在 React Native 中使用 TanStack Query,90% 的人只做了“安装”,而忽略了“配置”

如果不配置这两个管理器,你的 App 就失去了灵魂:

  1. AppStateStatus + focusManager:让 App 知道“我醒了”,从而实现微信切回来的自动刷新。
  2. setOnline + onlineManager:让 App 知道“我有网了”,从而实现走出电梯后的自动重连。

只要在 _layout.tsx 中写好这 50 行代码,你应用里所有的 useQuery 就都自动拥有了这些原生级别的感知能力。这就是从“能用”到“好用”的关键跨越。

Expo (React Native) 最佳实践:TanStack Query 深度集成指南

在 Expo 开发中,引入 TanStack Query (React Query) 是移动端开发的强烈推荐方案

它不仅仅是一个请求库,而是一套完整的异步状态管理方案,专门解决了 React Native 中最棘手的网络与缓存问题。

1. 为什么它是必选项?

在移动端场景下,TanStack Query 解决了传统 useEffect + useState 难以处理的四大痛点:

  • 智能缓存与离线支持:在网络不稳定或离线时展示缓存数据,避免白屏;网络恢复后自动在后台静默更新。
  • 应用激活自动刷新 (Focus Refetch) :当用户从后台(如回复微信后)切回 App 时,自动检测并刷新过期数据,确保用户看到的信息永远是最新的。
  • 状态管理自动化:彻底告别手写 isLoadingisErrordata 等繁琐的样板代码。
  • 列表交互集成:内置了对 下拉刷新无限滚动 (Infinite Scroll) 的完美支持,与 FlatList 配合天衣无缝。

2. 推荐的技术栈架构

在 React Native 应用中,我们推荐以下的“黄金三角”组合:

  • 状态管理 (Server State)TanStack Query (负责请求去重、缓存、过期策略)
  • 网络请求 (Fetcher)AxiosFetch (负责发送 HTTP 请求)
  • 安全存储 (Auth)Expo Secure Store (负责存储 Token)

3. 生产环境集成指南 (关键)

在 React Native 中使用 TanStack Query,必须手动配置 App 状态监听网络状态监听,否则“切后台自动刷新”等核心功能在真机上将失效。

第一步:安装依赖

# 核心库
npm install @tanstack/react-query

# 用于监听网络状态(必须)
npx expo install @react-native-community/netinfo

第二步:配置全局 Provider (app/_layout.tsx)

这是最关键的一步。我们需要告诉 TanStack Query 何时 App 处于“前台”,以及何时“联网”。

import { useEffect } from 'react';
import { AppStateStatus, Platform } from 'react-native';
import { Slot } from 'expo-router';
import { 
  QueryClient, 
  QueryClientProvider, 
  focusManager, 
  onlineManager 
} from '@tanstack/react-query';
import NetInfo from '@react-native-community/netinfo';
import { useAppState } from '@/hooks/useAppState'; // 假设你有一个 AppState Hook,或者直接写在下面

// 1. 配置网络状态监听 (解决断网重连自动刷新)
onlineManager.setEventListener((setOnline) => {
  return NetInfo.addEventListener((state) => {
    setOnline(!!state.isConnected);
  });
});

// 2. 配置 App 焦点监听 (解决切回前台自动刷新)
function onAppStateChange(status: AppStateStatus) {
  // Web 端自带监听,RN 端需要手动触发
  if (Platform.OS !== 'web') {
    focusManager.setFocused(status === 'active');
  }
}

// 3. 初始化 QueryClient (配置全局默认策略)
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 2, // 请求失败自动重试 2 次
      staleTime: 1000 * 60, // 数据 1 分钟内认为是“新鲜”的,不触发自动请求
      gcTime: 1000 * 60 * 5, // 缓存垃圾回收时间 (5分钟)
    },
  },
});

export default function RootLayout() {
  // 监听 App 状态变化
  useEffect(() => {
    const subscription = AppState.addEventListener('change', onAppStateChange);
    return () => subscription.remove();
  }, []);

  return (
    
       {/* 你的页面入口 */}
    
  );
}

第三步:在页面中使用

配合 RefreshControl 实现下拉刷新变得非常简单:

import { useQuery } from '@tanstack/react-query';
import { View, Text, FlatList, RefreshControl, ActivityIndicator } from 'react-native';
import axios from 'axios';

// 定义请求函数
const fetchTodos = async () => {
  const { data } = await axios.get('https://api.example.com/todos');
  return data;
};

export default function TodoListScreen() {
  const { data, isLoading, isError, refetch } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  });

  if (isLoading) return ;
  if (isError) return 加载失败,请重试;

  return (
     item.id.toString()}
      renderItem={({ item }) => {item.title}}
      // 集成下拉刷新,只需一行代码
      refreshControl={
        
      }
    />
  );
}

4. 总结

引入 TanStack Query 是 Expo 应用迈向高性能高可用性的关键一步。

  • 对于开发者:它消除了 90% 的冗余代码和手动状态管理。
  • 对于用户:它提供了丝滑的“切后台刷新”和“断网重连”体验。

Expo (React Native) 本地存储全攻略:普通数据与敏感数据该存哪?

在移动应用开发中,数据持久化(Data Persistence)是绕不开的话题。无论是为了让用户免于重复登录,还是保存用户的偏好设置(如深色模式),我们都需要将数据存储在用户的设备上。

对于使用 Expo 开发的 React Native 项目,官方推荐了两种截然不同的存储方案:AsyncStorageexpo-secure-store

本文将深入解析这两者的区别、适用场景,并教你如何在项目中优雅地封装它们。


方案一:AsyncStorage(通用存储)

AsyncStorage 是 React Native 社区维护的一个异步、非加密的键值对(Key-Value)存储系统。你可以把它想象成浏览器端的 localStorage

1. 适用场景

由于它是未加密的,任何拥有设备文件系统访问权限的人(或恶意软件)都能读取这些数据。因此,它只适合存储:

  • ✅ 用户偏好设置(如:是否开启推送、语言设置、主题色)
  • ✅ 应用缓存数据(如:首页的临时数据列表)
  • ✅ Redux/Zustand 状态的持久化
  • 绝对不要存:用户密码、身份认证 Token、信用卡信息

2. 安装

npx expo install @react-native-async-storage/async-storage

3. 使用示例

AsyncStorage 只能存储字符串。如果你要存储对象,需要使用 JSON.stringifyJSON.parse 进行转换。

import AsyncStorage from '@react-native-async-storage/async-storage';

// 1. 存储数据 (Key 必须是字符串,Value 也必须是字符串)
const saveSettings = async () => {
  try {
    await AsyncStorage.setItem('app_theme', 'dark');
    // 存储对象需序列化
    await AsyncStorage.setItem('user_profile', JSON.stringify({ name: 'John', age: 30 }));
  } catch (e) {
    console.error('保存失败', e);
  }
};

// 2. 读取数据
const loadSettings = async () => {
  try {
    const theme = await AsyncStorage.getItem('app_theme');
    
    const jsonValue = await AsyncStorage.getItem('user_profile');
    const userProfile = jsonValue != null ? JSON.parse(jsonValue) : null;
    
    console.log(theme, userProfile);
  } catch (e) {
    console.error('读取失败', e);
  }
};

// 3. 删除/清空
const clearData = async () => {
    await AsyncStorage.removeItem('app_theme'); // 删除单个
    await AsyncStorage.clear(); // 清空所有(慎用)
}

方案二:expo-secure-store(安全存储)

SecureStore 是 Expo 提供的一个库,它利用了操作系统底层的安全机制来加密存储数据。

  • iOS: 使用 Keychain Services(钥匙串)。
  • Android: 使用 SharedPreferences(配合 Keystore 加密)。

1. 适用场景

这是存储敏感数据的唯一正确选择:

  • ✅ 身份认证 Token (Auth Token / Refresh Token)
  • ✅ 用户密码 (Password)
  • ✅ API 密钥 (API Keys)

2. 安装

npx expo install expo-secure-store

3. 使用示例

API 与 AsyncStorage 非常相似,但它保证了数据是加密落盘的。

import * as SecureStore from 'expo-secure-store';

// 1. 存储敏感数据
const saveToken = async (token: string) => {
  // 注意:Key 和 Value 都必须是字符串
  await SecureStore.setItemAsync('user_token', token);
};

// 2. 读取数据
const getToken = async () => {
  const token = await SecureStore.getItemAsync('user_token');
  if (token) {
    console.log('获取到 Token:', token);
  } else {
    console.log('未找到 Token');
  }
};

// 3. 删除数据 (如用户登出时)
const removeToken = async () => {
  await SecureStore.deleteItemAsync('user_token');
};

深度对比:该选哪一个?

特性 AsyncStorage SecureStore
安全性 🚨 (明文存储,类似 TXT 文件) 🔒 (OS 级硬件加密)
存储容量 较大 (几 MB 到几十 MB 没问题) 极小 (建议只存 < 2KB 的短字符串)
读写速度 稍慢 (涉及解密过程)
数据类型 仅字符串 (需手动 JSON 序列化) 仅字符串
核心用途 缓存、设置、非敏感状态 Token、密码、证书

最佳实践:封装统一的存储工具

在真实的项目开发中,我们不应该在 UI 组件里直接调用 AsyncStorageSecureStore,而是应该封装一个工具模块。这样既能统一管理 Key,又能屏蔽底层实现的差异。

以下是一个推荐的 utils/storage.ts 封装示例:

// utils/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';

// 定义所有的 Key,防止手误写错
const KEYS = {
  TOKEN: 'auth_token',
  USER_INFO: 'user_info',
  THEME: 'app_theme',
};

/**
 * 敏感数据存储工具 (使用 SecureStore)
 */
export const AuthStorage = {
  async setToken(token: string) {
    await SecureStore.setItemAsync(KEYS.TOKEN, token);
  },

  async getToken() {
    return await SecureStore.getItemAsync(KEYS.TOKEN);
  },

  async clearToken() {
    await SecureStore.deleteItemAsync(KEYS.TOKEN);
  },
};

/**
 * 普通数据存储工具 (使用 AsyncStorage)
 */
export const AppStorage = {
  // 封装对象存储,自动处理 JSON 转换
  async setUser(user: any) {
    try {
      const jsonValue = JSON.stringify(user);
      await AsyncStorage.setItem(KEYS.USER_INFO, jsonValue);
    } catch (e) {
      console.error('Saving user failed', e);
    }
  },

  async getUser() {
    try {
      const jsonValue = await AsyncStorage.getItem(KEYS.USER_INFO);
      return jsonValue != null ? JSON.parse(jsonValue) : null;
    } catch (e) {
      console.error('Loading user failed', e);
      return null;
    }
  },
  
  async clearAll() {
      await AsyncStorage.clear();
  }
};

如何在业务中使用?

import { AuthStorage, AppStorage } from './utils/storage';

// 登录成功后
const handleLoginSuccess = async (apiResponse) => {
    const { token, user } = apiResponse;
    
    // 1. Token 进保险箱
    await AuthStorage.setToken(token);
    
    // 2. 用户信息进普通仓库
    await AppStorage.setUser(user);
    
    console.log('登录数据已持久化');
};

// 退出登录时
const handleLogout = async () => {
    await AuthStorage.clearToken();
    await AppStorage.setUser(null); 
};

总结

在 React Native (Expo) 开发中,请务必遵守 “数据分级” 原则:

  1. Token 和密码:必须通过 expo-secure-store 放入系统的“保险箱”。
  2. 设置和缓存:可以通过 @react-native-async-storage/async-storage 放入普通的“储物柜”。

通过合理的封装,你可以让代码更安全、更整洁,也更易于维护。

React Native 样式系统详解:与 Web CSS 的“似是而非”

很多从 Web 转战 React Native 的开发者最先问的问题通常是:“我能直接把 CSS 文件复制进去吗?”

答案是不能。虽然 React Native 的样式系统在命名和行为上极力模仿 CSS,但它本质上是JavaScript 对象,运行机制也完全不同。以下是关于这两者差异的完整技术总结。

1. 核心语法:从 Kebab-case 到 CamelCase

在 Web 中,CSS 是文本;在 RN 中,样式是代码(对象)。由于 JavaScript 对象的属性名不能包含连字符(-),所有 CSS 属性都必须转换为 小驼峰命名法 (camelCase)

特性 Web CSS React Native Style
背景色 background-color: red; backgroundColor: 'red'
字体大小 font-size: 16px; fontSize: 16 (注意是数字)
外边距 margin-top: 20px; marginTop: 20
复合属性 border: 1px solid red; 不支持。必须拆分为 borderWidth, borderColor, borderStyle

为什么这样做?

因为样式是 JS 对象,这意味着你可以利用编程语言的所有能力:变量、条件判断、函数计算等。

// React Native 允许动态计算样式
<View style={{
  backgroundColor: isActive ? 'blue' : 'gray', // 条件样式
  width: windowWidth * 0.5 // 动态计算
}} />

2. 继承与层叠:数组覆盖法

Web CSS 的全称是“层叠样式表”(Cascading Style Sheets),依赖选择器权重(Specificity)来决定谁生效。

React Native 没有选择器(没有 .class 或 #id),也没有隐式的样式继承(子元素不会自动继承父元素的字体颜色)。

RN 的“层叠”通过数组实现:

RN 允许你给 style 属性传递一个数组。数组中越靠后的样式优先级越高。

const styles = {
  base: { fontSize: 14, color: 'black' },
  active: { color: 'blue' } // 激活状态覆盖颜色
};

// 数组最后一个生效,最终颜色为 blue
<Text style={[styles.base, styles.active]}>Hello</Text>

这种方式让样式覆盖变得显式且可预测,彻底消除了 Web 开发中“不知道这个样式是从哪里继承来的”痛苦。

3. 布局系统:Flexbox 是唯一真理

React Native 移除了 Web 中复杂的 float, display: block/inline, grid 等布局方式,只保留并强制使用 Flexbox

但有一个巨大的陷阱需要注意:默认主轴方向不同

  • Web Flexbox: 默认 flex-direction: row (横向排列)。

  • RN Flexbox: 默认 flexDirection: 'column' (纵向排列)。

    • 原因: 手机屏幕是窄长的,垂直滚动是移动端的默认交互模式。

4. 尺寸与单位:没有 px,只有逻辑点

在 Web 上,我们纠结于 px, em, rem, vw, vh。

在 RN 上,几乎所有尺寸属性(width, height, margin, padding, fontSize)都只接受不带单位的数字。

  • 含义: 这些数字代表 逻辑像素 (Logical Pixels / Points)

  • 自动适配: RN 会根据设备的屏幕密度(DPI/PixelRatio)自动将其转换为屏幕上的物理像素。

    • width: 100,在普通屏是 100px,在 Retina 屏可能是 200px 或 300px。
    • 例外: 也可以使用百分比字符串,如 width: '50%'

5. 常见痛点与已知限制 (Known Issues)

根据你提供的文档片段,RN 并不是完美复刻了 CSS 引擎,这里有几个著名的“坑”:

A. 触摸区域与父级边界 (Parent Bounds)

  • Web: 子元素设为 absolute 并移出父元素框外,通常依然可见且可点击。

  • RN (Android): 子元素的触摸事件无法超出父组件的边界。如果你把按钮用 position: absolute 移到了父 View 的外面,你看着它在那里,但点它没反应。

    • 注: 视觉上,Android 默认 overflow: hidden 行为较强,虽新版本有改善,但点击判定依然严格遵循父级区域。

B. 负边距 (Negative Margins)

  • Web: margin-top: -50px 是常用的重叠布局技巧。
  • RN: 文档明确提到 "on Android negative margin is not supported" (或支持受限)。虽然现代 RN 版本对负 margin 的支持已经好转,但在某些复杂嵌套或旧版本 Android 上,它依然会导致布局塌陷或裁剪。

C. 圆角与图片 (Border Radius)

如前文所述,iOS 的 <Image> 组件对 borderTopLeftRadius单独圆角属性支持不佳。必须通过包裹一个 <View> 并设置 overflow: 'hidden' 来实现异形图片。

D. 阴影 (Shadows)

这是最分裂的地方:

  • iOS: 使用 shadowColor, shadowOffset, shadowOpacity (类似 CSS)。
  • Android: 必须使用 elevation (一个数字,对应 Material Design 的层级高度)。为了跨平台,通常需要根据平台写两套代码。

6. 最佳实践:StyleSheet.create

虽然你可以直接写内联样式对象 style={{color: 'red'}},但官方推荐使用 StyleSheet.create

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

为什么?

  1. 性能: 系统可以将这些样式对象 ID 化并缓存,避免每次渲染组件时都重新创建新的对象。
  2. 验证: 它会在开发阶段检查你的属性名是否合法(比如如果你写了 background-color,它会直接报错提醒你改成 backgroundColor)。

总结

当你开始在 React Native 中写样式时,请记住:

  1. ❌ 不要用 Kebab-case (font-size),要用 CamelCase (fontSize)。
  2. ❌ 不要加 px 单位,直接写数字
  3. ❌ 不要指望样式自动继承(Text 组件内的嵌套除外)。
  4. ⚠️ 默认布局是 纵向 (Column) 的。
  5. ⚠️ 所有的边框、阴影、圆角,在 iOS 和 Android 上可能表现不一致,多真机测试。

React Native 图片机制深度解析:设计哲学、性能优化与避坑指南

1. 核心哲学:体验优先于便利 (UX > DX)

React Native 在图片处理上与 Web 浏览器有着本质的不同。RN 宁愿让开发者多写一点代码,也要保证用户体验的极致顺滑。

  • 拒绝“布局抖动” (No Layout Shift):

    • Web 痛点: 浏览器加载图片时默认是 0x0,下载完瞬间撑开,导致页面跳动。
    • RN 策略: 强制要求开发者预先指定远程图片的宽高。这意味着图片加载前位置就已留好,加载后只是填空,界面纹丝不动。
  • 例外: 本地静态图片(require('./icon.png'))因在编译时已知尺寸,可自动推断宽高。

2. 底层性能:为了不卡顿 (Performance)

RN 在幕后做了大量工作,确保即使加载高清大图,App 的 UI 线程(Main Thread)也不会阻塞。

  • 后台解码 (Off-thread Decoding):

    • 图片解码(JPEG/PNG转像素)非常耗时。RN 将其移至后台线程执行,解码完成后再送回主线程显示。这避免了 Web 常见的“滚动时因加载图片导致的掉帧”。
  • iOS 智能选图 (The 50% Rule):

    • 从相册加载图片时,RN 不会无脑加载原图(太费内存),也不会选太小的图(太糊)。
    • 它会自动寻找第一张比显示区域大 50% 以上的缩略图版本。既保证了清晰度(避免拉伸模糊),又最大程度节省了内存。

3. 架构设计:面向未来的扩展性 (Extensibility)

  • Source 是对象而非字符串:

    • <Image source={{uri: '...'}} /> 的设计看似繁琐,实则为了扩展。
    • 它允许携带元数据(Metadata),并为未来特性(如雪碧图裁剪 crop 属性)预留了接口,保证了代码的向后兼容性。

4. iOS 实战:避坑与进阶调优 (iOS Specifics)

针对 iOS 平台,有一些特殊的限制和高级配置需要注意:

  • 圆角样式的坑:

    • 问题: borderTopLeftRadius 等单独圆角属性在 iOS <Image> 上往往不生效。
    • 解法: 使用“外部裁剪法”。将 Image 包裹在 <View> 中,在 View 上设置圆角并加上 overflow: 'hidden'
  • 手动控制缓存 (Cache Limits):

    • 能力: 可以在 AppDelegate.m 中调用 RCTSetImageCacheLimits
    • 参数: 可以设定“单张图片最大体积”(超过不缓存)和“总缓存池上限”(超过踢掉旧图),从而在内存紧张或图片密集的 App 中找到性能平衡点。

总结

React Native 的 Image 组件不仅仅是一个简单的 UI 元素,它是一个高度封装的、自带性能优化策略的子系统。理解这些机制,能帮你写出即便在数千张图片的瀑布流中依然如丝般顺滑的 App。

❌