03 性能、动画与 React Native 新架构

React Native 性能不是只看 React 重新渲染。移动端还要关注 JS 线程、UI 线程、原生模块调用、列表虚拟化、图片内存、动画流畅度和启动时间。本章覆盖从性能基础到 New Architecture 的专家能力。

1. 线程模型

React Native 传统心智中常见线程:

  • JS Thread:运行 JavaScript、React 渲染、业务逻辑。
  • UI/Main Thread:原生 UI 绘制和交互。
  • Native Modules Thread:部分原生模块工作。
  • Shadow Tree / Layout:布局计算相关工作。

如果 JS Thread 被长任务阻塞,点击、导航、列表更新可能卡顿。若动画依赖 JS 每帧驱动,也会掉帧。

2. 性能指标

移动端关注:

  • 冷启动时间。
  • 首屏可交互时间。
  • JS bundle 体积。
  • 列表滚动 FPS。
  • 导航切换耗时。
  • 图片内存。
  • 崩溃率。
  • ANR / 卡死。
  • 电量和发热。

3. 常见性能问题

  • ScrollView 渲染长列表。
  • FlatList 配置不当。
  • renderItem 不稳定。
  • 列表项太复杂。
  • 图片过大。
  • JS 同步计算太重。
  • 频繁跨 JS/native 边界。
  • Context 高频更新。
  • 动画跑在 JS 线程。
  • 启动时加载过多模块。

4. FlatList 优化

jsx 复制代码
const renderItem = useCallback(({ item }) => {
  return <LessonCard item={item} />;
}, []);

const keyExtractor = useCallback((item) => item.id, []);

<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  initialNumToRender={8}
  maxToRenderPerBatch={8}
  windowSize={7}
  removeClippedSubviews
/>

固定高度:

jsx 复制代码
const ITEM_HEIGHT = 88;

<FlatList
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

列表优化顺序:

  1. FlatList 替代长 ScrollView
  2. 稳定 renderItemkeyExtractor
  3. 简化列表项。
  4. 使用 getItemLayout
  5. 调整 batch/window 参数。
  6. 必要时使用 FlashList。

5. React.memo

jsx 复制代码
const LessonCard = memo(function LessonCard({ item, onPress }) {
  return (
    <Pressable onPress={() => onPress(item.id)} style={styles.card}>
      <Text>{item.title}</Text>
    </Pressable>
  );
});

要让 memo 有效,传入 Props 要稳定。

错误:

jsx 复制代码
<LessonCard item={{ ...item }} onPress={() => open(item.id)} />

6. useMemo / useCallback

jsx 复制代码
const visibleItems = useMemo(
  () => filterLessons(items, query, level),
  [items, query, level],
);

const handlePress = useCallback((id) => {
  navigation.navigate('Detail', { id });
}, [navigation]);

不要盲目使用。优化前先用 Profiler、React Native DevTools 或 Flipper 定位。

7. InteractionManager

把非紧急任务延后到交互之后。

jsx 复制代码
useEffect(() => {
  const task = InteractionManager.runAfterInteractions(() => {
    warmUpSearchIndex();
  });

  return () => task.cancel();
}, []);

适合导航动画后再执行重计算。

8. 图片性能

图片问题常见于移动端:

  • 原图过大。
  • 列表头像重复加载。
  • 没有占位图。
  • 图片尺寸不明确。
  • 缓存策略差。

基础:

jsx 复制代码
<Image
  source={{ uri: item.cover }}
  style={{ width: 96, height: 72, borderRadius: 8 }}
  resizeMode="cover"
/>

专家实践:

  • 服务端裁剪。
  • WebP/AVIF 支持视平台而定。
  • 缩略图优先。
  • 列表中避免大图。
  • 使用成熟图片库处理缓存。

9. Animated

React Native 内置 Animated:

jsx 复制代码
const opacity = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.timing(opacity, {
    toValue: 1,
    duration: 220,
    useNativeDriver: true,
  }).start();
}, [opacity]);

return <Animated.View style={{ opacity }} />;

useNativeDriver: true 能把支持的动画交给原生侧,减少 JS 线程影响。

限制:

  • 不是所有样式都支持 native driver。
  • 布局相关动画通常更复杂。

10. Reanimated

复杂手势和高性能动画常用 Reanimated。

概念:

jsx 复制代码
const translateX = useSharedValue(0);

const animatedStyle = useAnimatedStyle(() => ({
  transform: [{ translateX: translateX.value }],
}));

优势:

  • 动画逻辑运行在 UI 线程。
  • 更适合手势驱动动画。
  • 与 Gesture Handler 配合好。

11. Gesture Handler

复杂手势不要只靠 Pressable。

常见手势:

  • Pan。
  • Tap。
  • LongPress。
  • Pinch。
  • Swipe。

手势设计要考虑:

  • 与 ScrollView 冲突。
  • Android/iOS 手势差异。
  • 触摸区域。
  • 可访问性替代操作。

12. LayoutAnimation

简单布局动画:

jsx 复制代码
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded((value) => !value);

适合简单展开收起。复杂场景用 Reanimated。

13. 启动性能

启动优化:

  • 减少启动时同步计算。
  • 减少首屏依赖。
  • 延迟加载低频模块。
  • 优化图片和字体。
  • 使用 Hermes。
  • 控制 bundle 体积。
  • 避免启动时请求阻塞首屏。

14. Hermes

Hermes 是 React Native 常用 JavaScript 引擎,优化启动和内存表现。

关注点:

  • 与 RN 版本兼容。
  • Debug 和 Release 表现不同。
  • 性能测试以 Release 包为准。

15. Metro

Metro 是 React Native bundler。

常见能力:

  • 模块解析。
  • 平台文件选择:.ios.js / .android.js
  • Babel 转换。
  • 资源打包。

专家实践:

  • 控制依赖体积。
  • 避免引入 Node-only 包。
  • 配置 monorepo watchFolders。
  • 使用 bundle 分析工具。

16. New Architecture

React Native New Architecture 包括:

  • Fabric Renderer:新渲染系统。
  • TurboModules:新原生模块系统。
  • JSI:JavaScript Interface,减少传统 bridge 开销。
  • Codegen:类型安全地生成 JS/native 绑定。

官方在 RN 0.76 起默认启用新架构,并带来现代 React 能力支持,如 Suspense、Transitions、automatic batching、useLayoutEffect 等。

17. 旧 Bridge 的限制

传统 Bridge 特点:

text 复制代码
JS <-> serialized async bridge <-> Native

限制:

  • 通信需要序列化。
  • 高频调用成本高。
  • 同步能力受限。
  • 类型边界弱。

18. JSI

JSI 允许 JS 引擎和原生对象更直接交互。

价值:

  • 避免大量 JSON 序列化。
  • 支持同步调用场景。
  • 为高性能库提供底层能力。

典型使用者:

  • Reanimated。
  • MMKV。
  • Vision Camera。

19. TurboModules

新原生模块系统:

  • 懒加载模块。
  • 类型安全。
  • Codegen 生成绑定。
  • 更少 bridge 开销。

适合封装平台能力,例如加密、传感器、SDK。

20. Fabric

Fabric 是新渲染器:

  • 与 React 并发特性更好集成。
  • 更一致的布局和渲染路径。
  • 支持更现代的原生组件系统。

对应用开发者:

  • 大多数业务代码不用直接操作 Fabric。
  • 依赖库需要兼容新架构。
  • 升级时重点检查原生库兼容。

21. Codegen

Codegen 根据类型定义生成 native binding。

价值:

  • 类型安全。
  • 减少手写桥接。
  • JS 和 Native API 一致。

专家实践:

  • Native Module API 先设计稳定。
  • 入参和返回值保持可序列化或受支持类型。
  • 错误模型明确。

22. Native Module 何时需要

需要原生模块的情况:

  • JS 无法访问的平台能力。
  • 性能关键逻辑。
  • 第三方原生 SDK。
  • 加密、安全存储、媒体处理。
  • 复杂传感器和硬件交互。

不需要:

  • 普通业务状态。
  • 可以用现有 Expo/community 包解决。
  • 只是为了"高级"。

23. 性能排查顺序

  1. 用 Release 包复现。
  2. 确认是 JS 卡、UI 卡、网络慢、图片慢还是原生模块慢。
  3. 检查列表。
  4. 检查图片。
  5. 检查启动依赖。
  6. 检查频繁 setState / Context 更新。
  7. 检查原生库和新架构兼容。
  8. 做小范围优化并验证指标。

24. 专家性能清单

  • 长列表不使用 ScrollView。
  • 图片有尺寸、缓存、缩略图策略。
  • 动画尽量不依赖 JS 每帧驱动。
  • 首屏不加载低频模块。
  • Context 不承载高频变化。
  • 大计算延后或移出 JS 主路径。
  • 新架构升级前检查依赖兼容。
  • 性能测试使用 Release 构建。

25. FlatList 问题排查库

问题:滚动卡顿。

排查:

  • 是否使用了 ScrollView
  • renderItem 是否每次创建复杂闭包?
  • item 是否包含大图、阴影、复杂 SVG?
  • 是否缺少 keyExtractor
  • 是否可以使用 getItemLayout
  • initialNumToRender 是否过大?
  • 是否在列表滚动时 setState?

优化:

jsx 复制代码
const LessonRow = memo(function LessonRow({ item, onPress }) {
  return <Pressable onPress={() => onPress(item.id)}>{/* ... */}</Pressable>;
});

26. 动画性能扩展

动画分三类:

  • 简单透明度和位移:Animated + native driver。
  • 手势驱动:Reanimated + Gesture Handler。
  • 布局展开收起:LayoutAnimation 或 Reanimated Layout Animations。

反模式:

jsx 复制代码
setInterval(() => {
  setPosition((x) => x + 1);
}, 16);

这会让 JS 每帧 setState,容易卡顿。

27. 启动性能扩展

启动阶段不要做:

  • 大量 JSON parse。
  • 同步初始化多个 SDK。
  • 立即加载低频页面。
  • 首屏前请求太多接口。
  • 渲染隐藏的大组件树。

启动优化路线:

  1. 首屏最小化。
  2. SDK 延迟初始化。
  3. 低频模块 lazy。
  4. 图片和字体优化。
  5. Release 包测量。

28. 新架构迁移风险

迁移前检查:

  • 所有原生依赖是否支持 New Architecture。
  • 是否使用旧 Native Module。
  • 是否依赖不兼容的 UI 组件库。
  • iOS Pods 和 Android Gradle 是否支持。
  • CI 是否能构建新架构。

迁移策略:

  • 单独分支验证。
  • 先升级 RN 版本。
  • 再启用新架构。
  • 保留回退开关。
  • 优先修复核心路径。

29. 原生模块设计原则

Native Module API 应:

  • 小而稳定。
  • 参数类型明确。
  • 错误模型明确。
  • 避免高频跨边界调用。
  • 支持取消或超时。

错误:

js 复制代码
NativeModule.doEverything(bigObject);

更好:

js 复制代码
NativeCrypto.encrypt({ text, keyId });
NativeCrypto.decrypt({ payload, keyId });

30. 性能专家题

  • 卡顿发生在 JS 线程还是 UI 线程?
  • Debug 包和 Release 包表现是否一致?
  • FlatList item 是否过重?
  • 动画是否依赖 JS 每帧执行?
  • 图片是否经过裁剪和缓存?
  • 新架构是否改变了依赖兼容性?
  • 是否有启动阶段同步重任务?

31. 性能知识点索引

  1. JS Thread 阻塞会影响交互。
  2. UI Thread 掉帧会影响动画。
  3. Debug 包性能不可信。
  4. Release 包才是性能依据。
  5. FlatList 参数需要按场景调。
  6. FlashList 可用于超大列表。
  7. 图片内存是移动端大头。
  8. 动画应尽量跑 UI 线程。
  9. Reanimated 适合复杂手势动画。
  10. Gesture Handler 处理手势冲突。
  11. InteractionManager 延后非紧急任务。
  12. Hermes 优化启动和内存。
  13. Metro 影响模块打包。
  14. JSI 减少传统 bridge 开销。
  15. TurboModules 提供新模块系统。
  16. Fabric 是新渲染器。
  17. Codegen 提高 native binding 类型安全。
  18. 新架构依赖兼容必须检查。
  19. 启动阶段要减少同步任务。
  20. 性能优化必须有指标对比。

32. 新架构专家问题

  • 当前依赖是否全部支持 New Architecture?
  • 是否存在旧 bridge 高频调用?
  • Native Module API 是否过大?
  • 是否需要同步 native 能力?
  • Codegen 类型是否覆盖错误模型?
  • Fabric 迁移是否影响自定义组件?
  • 回退策略是什么?

面试题完整答案总集:React Native 性能、动画与新架构

卡顿发生在 JS 线程还是 UI 线程,如何判断?

如果点击响应、状态更新、列表渲染慢,通常怀疑 JS 线程;如果动画和滚动掉帧但 JS 日志不多,可能是 UI 线程或原生绘制压力。应在 Release 包中使用 Profiler、Flipper、Xcode Instruments、Android Profiler 等工具定位,而不是凭感觉判断。

Debug 包和 Release 包表现是否一致?

通常不一致。Debug 包有开发工具、日志、未优化打包和调试开销,性能不能代表真实用户体验。性能结论必须基于 Release 或接近 Release 的构建。

FlatList item 是否过重会造成什么问题?

列表项如果包含大图、复杂阴影、嵌套过深、频繁状态更新或匿名函数,会导致滚动掉帧和内存增长。优化方式包括 memo 列表项、稳定 renderItem、简化布局、固定行高、缩略图和合理 FlatList 参数。

动画是否应该依赖 JS 每帧执行?

不应该。JS 每帧 setState 容易被业务逻辑阻塞,导致动画掉帧。简单动画可用 Animated native driver,复杂手势动画优先用 Reanimated,让动画逻辑运行在 UI 线程。

图片为什么是移动端性能重点?

图片会占用网络、解码时间和内存。列表中加载原图会导致卡顿和内存压力。应使用服务端裁剪、缩略图、明确尺寸、缓存策略、占位图和失败状态。

新架构会改变哪些风险?

新架构带来 Fabric、TurboModules、JSI、Codegen 等能力,但也要求原生依赖兼容。迁移风险包括旧 Native Module 不兼容、UI 组件库不支持 Fabric、Pods/Gradle 配置问题、CI 构建失败。迁移前必须检查依赖和保留回退方案。

启动阶段同步重任务为什么危险?

启动阶段如果同步解析大 JSON、初始化多个 SDK、加载低频模块,会延迟首屏和可交互时间。应最小化首屏依赖,把非关键任务延迟到交互后或后台执行。

Native Module API 应如何设计?

API 应小而稳定,参数和返回值类型明确,错误模型清晰,避免高频跨边界调用。原生模块应封装平台能力,不应随某个页面业务频繁变化。复杂任务应支持取消、超时和错误上报。

相关推荐
萑澈2 小时前
Ripple新前端框架的发展与AI原生全栈开发前景:架构重塑与生产力范式转移研究报告
架构·前端框架·ai-native
weixin_446260853 小时前
DeepDive:深度解析 DeepSeek V4 架构革新与长文本时代的算力重塑
架构
空中海3 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
狂奔solar3 小时前
从“钢筋安装质量验收标准“谈起:知识库问答“多跳检索”架构演进与实践
架构·知识图谱·知识库溯源
勤劳打代码4 小时前
Flutter 架构日记 —— 可演进的 Flutter Dialog 组件
flutter·架构
空中海4 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
gQ85v10Db4 小时前
Redis分布式锁进阶第十四篇:全系列终局架构复盘 + 锁体系统一规范 + 线上全年零事故收官方案
redis·分布式·架构
人道领域5 小时前
从零构建高可用Agent:后端架构实战与避坑指南
架构·langchain·agent