构建高性能的 React Native 应用

发布: (2026年1月9日 GMT+8 02:02)
12 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体内容,我将为您翻译成简体中文,并保持原有的格式、Markdown 语法和技术术语不变。

概览

在决定提升性能之前,先了解应用的哪一部分真的慢

  • React Debugger – 帮助你发现耗时的重新渲染、首次加载时间以及页面挂载/卸载状态。
    不要低估它的价值;它可以显著提升性能。

React native flame graph

如果想超越 JavaScript 层面,你可以使用 Xcode 或 Android Studio 对应用进行原生分析。原生分析可以让你看到:

  • 总内存使用量
  • 某个特定页面或操作的内存消耗
  • 原生内存泄漏
  • CPU 使用率

原生分析是可选的,如果你的应用没有执行大量操作(例如视频渲染或 3D 图形),可以跳过。

你可以使用 Android StudioXcode 等 IDE 对应用进行原生分析。这样做可以让你清晰了解整体内存使用情况、原生泄漏以及 CPU 负载。

Xcode screenshot

Android Studio screenshot

此时,你应该已经对应用的哪一部分慢有了大致了解。接下来我们来讨论如何以及怎样进行优化

渲染大型列表

渲染列表可能具有挑战性。如果你的项目项很简单,使用 FlatListFlashList(Shopify)。两者都是处理中等规模列表的直接方案。

当每个项的渲染需要显著时间(例如约 400 ms)时,问题就会出现。FlatList 的虚拟化会在用户滚动时添加/移除项,这会导致 CPU 正忙于虚拟化,从而出现可见的空白。

解决方案

Library关键特性帮助原因
FlashList项目 回收重用已渲染的项目,降低创建新项目的开销。
LegendList响应式 与可选回收当没有变化时防止项目重新渲染,消除空白。
Legend‑State细粒度响应式与 LegendList 配合使用,实现实时数据更新而无需不必要的重新渲染。

LegendList 是一种强大的解决方案,可高效渲染大量项,通过精确定位和可选的回收机制有效消除空白。将其与 Legend‑State 结合使用,可在需要细粒度响应式(例如实时数据流)的场景下提供实时数据更新且不会产生不必要的重新渲染。

初始启动时间

Huddle01 应用的动图

启动时间 是指您的应用在设备上加载并运行所需的时间。它包括在 RAM 中加载和执行 JavaScript 包。

如果您的应用随附了 臃肿的 JS 包,用户会感受到明显的延迟,导致沮丧。

减少包大小和启动时间的技巧

  1. 启用 Hermes(或其他 JS 引擎)以加快启动速度。
  2. 使用代码拆分import() 语句)来懒加载屏幕。
  3. 移除未使用的依赖npm pruneyarn autoclean)。
  4. 启用 Metro 的 inlineRequires 以推迟模块初始化。
  5. 压缩资源(图片、字体),并尽可能使用矢量图形。
  6. 使用 npx react-native-bundle-visualizer 分析包,找出大型模块。

JS 打包工具截图

最后思考

性能优化是一个 迭代过程

  1. 分析 → 确定瓶颈。
  2. 应用针对性修复(列表虚拟化、包体积缩减、原生分析)。
  3. 重新分析 以验证改进。

遵循上述步骤,你将交付一款在高端和中端设备上都运行流畅的 React Native 应用。祝编码愉快!

Bundle Size

  • 使用 webpack‑bundle‑analyzer 等工具查看哪些部分在你的 bundle 中占比最大。
  • 找出体积较大的第三方依赖,并用更轻量的替代方案替换它们。

注意: 与网页不同,React Native 开箱即支持完整的代码拆分(code‑splitting)。

  • 你可以尝试使用 Repack 打包工具,它提供代码拆分支持(需要额外配置)。

最佳实践:

  • 将媒体资源(图片、SVG 等)从 JavaScript bundle 中剥离。
  • 将它们托管在远程服务器,并使用 expo‑image 等库进行缓存。

懒加载的幻觉

在构建 Huddle01 Meet 应用时,我们注意到 Android 设备上出现了明显的卡顿。原因是什么?一次性渲染 所有 底部弹窗组件。

解决方案

{showSheet && <BottomSheet />}
  • 只渲染当前可见的弹窗。
  • Android 设备(尤其是中低端机型)内存和 CPU 受限,条件渲染可以显著提升流畅度。

经验法则: 不要渲染一开始不可见的 UI。
这种技巧我称之为 “懒加载的幻觉”。 明智地使用它,并结合 Intersection Observer API(或其 React‑Native 等价实现),可以彻底避免挂载屏幕外的组件。

语法糖的取舍

  • 避免仅提供语法糖的“花哨”JavaScript库。
  • 记住:JS 占 React‑Native 应用运行时约 80 %,因此每增加一个库都会影响性能。

建议

  • 倾向于使用自定义实现,而不是笨重的第三方包。
  • 在低端 Android 设备上跳过 CSS‑in‑JS 库,除非它们真的很轻量(例如 react‑native‑unistyle)。
  • 在添加任何依赖之前进行测量——运行时开销是性能的最大敌人。

不要为你的目标受众(例如 $100‑range 手机)永远不会使用的功能进行优化。

命令式 UI 更新

React 的声明式模型表现出色,但有时完整的重新渲染代价太高。

  • setNativePropslink)允许你在不触发 React 渲染的情况下修改原生视图。
  • 谨慎使用它;它可能导致 React 状态与 UI 不同步。

**典型用例:**在低端设备上切换暗/亮模式。通过 React 状态更新整个 UI 可能会出现明显的卡顿;命令式更新可能更快,但必须手动保持 React 状态同步。

动画

  • react‑native‑reanimated 将动画工作卸载到 UI 线程,这对高端设备非常有利。
  • 在低端 Android 手机上,额外的线程开销可能适得其反,导致动画卡顿或无法运行。

指南

  • 如果你的主要受众使用低端硬件,请坚持使用 vanilla React‑Native 动画Animated API),以减少线程争用。

Leveraging GPU Resources

来自 margelo.com 的 GPU 图像

  • react‑native‑skia 让你直接在 JavaScript 中使用 GPU 绘制图形,实现实时图像处理和着色器效果。
  • react‑native‑filament(由 Margelo 开发)在原生 GPU 上渲染完整的 3D 场景——想象一下 “Pokemon GO‑style” 的体验。

Why it matters: 将繁重的视觉工作卸载到 GPU 可以减轻 JavaScript 线程的压力,从而在资源受限的设备上实现更流畅的 UI。

C++ 魔法与新架构

React Native 的 新架构(TurboModules、Fabric、JSI)允许您使用 C++ 编写性能关键代码,并几乎零开销地将其暴露给 JavaScript。

  • 使用 TurboModules 处理需要频繁调用的原生模块。
  • 利用 JSI(JavaScript 接口)在 C++ 与 JS 之间桥接,避免桥接瓶颈。

结果: 更快的原生到 JS 通信,降低延迟,并更好地利用设备资源。

低端 Android 优化快速检查清单

操作
1分析 bundle 大小;剔除体积大的依赖。
2将媒体资源托管在远程;使用 expo-image 进行缓存。
3仅渲染可见组件(条件渲染、Intersection Observer)。
4避免不必要的“语法糖”库;在添加前进行测量。
5仅在能够保持 React 状态同步时使用 setNativeProps
6在低端设备上,优先使用原生 Animated API 而非 Reanimated。
7使用 react-native-skiareact-native-filament 将图形卸载到 GPU。
8采用新架构(TurboModules、Fabric、JSI)以实现 C++ 级别的性能。

React Native、JSI 与 C++ 的强大力量

JSI(JavaScript Interface) 消除了传统桥接的需求,使得可以直接从 JavaScript 运行时访问原生 API。它使用 C++ 编写,提供了一种高性能、低开销的调用原生代码的方式。

最有经验的 C++ 工程师之一、React Native 的核心贡献者 Marc Mrousavy 以及他的团队发布了多个强大的库,这些库利用了完全用 C++ 编写的 JSI 模块。

  • react‑native‑quick‑crypto – 一个 C++ 实现,在原生端完成所有繁重工作,使 JavaScript 线程保持完全非阻塞。
  • Nitro Modules – 一种使用高度优化的 JSI 编写原生模块的新方式,端到端类型安全。基准测试请查看 这里

为什么 C++ 如今如此重要

得益于 AI 辅助开发,基于特定需求生成原生模块变得前所未有的容易。Nitro Modules 加速了这一过程,使原生代码的编写更快、更稳健。

原生代码与 JavaScript 之间的界限正在模糊。C++ 正是那座桥梁,让编写自定义原生实现变得直接,从而推动 React Native 能力的极限。

结论

过去五年是 React Native 的黄金时期。在社区的支持下,我们现在拥有了真正 “写一次,运行于任何平台” 的代码库。随着我们变得更加有创意,我们必须推动现有技术的极限。React Native 在某些领域仍可能存在限制,但它将继续改进。未来的继任者可能最终克服我们今天面临的挑战。看到像 React Native 和 Flutter 这样的跨平台技术不断演进,令人着迷。

感谢阅读本博客。欢迎在评论区提出任何问题。

新年快乐!

Back to Blog

相关文章

阅读更多 »