# React Native 八股文 · 全面深入指南

0 阅读1小时+

React Native 八股文 · 全面深入指南

涵盖底层原理、第三方库、难点问题、性能优化、横向对比、高难度深底层原理


目录


第一部分:最全面最底层的八股文

1.1 React Native 整体架构演进

1.1.1 旧架构(Bridge 架构)

React Native 旧架构的核心是 Bridge(桥接),整体分为三层:

  • JS 线程(JS Thread):运行 JavaScript 代码,包含业务逻辑、React 组件树的 diff 计算。JS 引擎在 iOS 上默认使用 JavaScriptCore(JSC),Android 上可选 JSC 或 Hermes。
  • Shadow 线程(Shadow Thread):运行 Yoga 布局引擎,负责将 Flexbox 布局声明转换为具体的像素坐标。Shadow Thread 维护一棵 Shadow Tree,是 JS 端虚拟 DOM 到原生视图之间的中间表示。
  • 主线程(Main/UI Thread):负责原生视图的创建、更新、渲染,处理触摸事件等。

Bridge 的通信机制:

  1. JS 线程将操作序列化为 JSON 消息,放入消息队列
  2. Bridge 将消息从 JS 线程异步传递到 Native 线程
  3. Native 线程反序列化 JSON,执行对应的原生操作
  4. 返回结果同样经过序列化 → Bridge → 反序列化的过程

Bridge 的瓶颈:

  • 序列化/反序列化开销:所有数据都要经过 JSON 序列化,大数据量时性能急剧下降
  • 异步通信延迟:Bridge 通信是异步批量的,不能实时响应,滑动列表时容易出现白屏
  • 单线程瓶颈:JS 线程和 UI 线程之间通过单一 Bridge 通信,存在排队等待
  • 无法同步调用:JS 无法同步获取原生数据(如布局尺寸),必须回调

1.1.2 新架构(Fabric + TurboModules + JSI + Codegen)

JSI(JavaScript Interface):

  • JSI 是一个轻量级的 C++ 抽象层,允许 JS 直接持有 C++ 对象的引用(Host Objects)
  • JS 可以直接调用 C++ 方法,无需序列化/反序列化
  • JSI 与 JS 引擎解耦,可以替换底层引擎(JSC → Hermes → V8)
  • 通过 JSI,JS 可以同步调用 Native 方法,也可以异步调用

TurboModules:

  • 替代旧的 NativeModules 系统
  • 基于 JSI 实现,JS 可以直接持有 Native Module 的引用
  • 支持懒加载:模块在首次访问时才初始化,而非应用启动时全部初始化
  • 支持同步调用和异步调用
  • 通过 Codegen 自动生成类型安全的接口代码

Fabric 渲染器:

  • 替代旧的 UI Manager
  • 基于 JSI 实现,JS 端可以直接操作 Shadow Tree
  • 支持同步渲染和异步渲染(Concurrent Mode 兼容)
  • 渲染流水线:Render → Commit → Mount
    • Render 阶段:在 JS 线程创建 React Element Tree,生成 React Shadow Tree(不可变的 C++ 对象)
    • Commit 阶段:在后台线程进行布局计算(Yoga),提升 Shadow Tree,执行 Tree Diffing
    • Mount 阶段:在主线程将 Shadow Tree 差异应用到 Host View Tree(原生视图树)
  • Shadow Tree 是不可变的(Immutable),每次更新创建新树,通过 diff 算法计算差异

Codegen:

  • 编译时工具,根据 Flow/TypeScript 类型定义自动生成 C++、Java、ObjC 的胶水代码
  • 保证 JS 和 Native 之间的类型安全
  • 减少手写 Bridge 代码的错误

1.1.3 Bridgeless 模式

  • 新架构的最终形态是完全移除 Bridge
  • 所有通信通过 JSI 直接进行
  • 旧的 NativeModules 通过互操作层(Interop Layer)桥接到新架构
  • 实现了真正的 JS ↔ Native 直接通信

1.2 JavaScript 引擎

1.2.1 JavaScriptCore (JSC)

  • WebKit 项目的一部分,iOS 上默认引擎
  • iOS 上使用系统自带的 JSC framework,不可定制
  • Android 上使用打包在 APK 中的 JSC,可以替换
  • 不支持 JIT(Just-In-Time)编译优化(iOS 限制)
  • 内存占用相对较高

1.2.2 Hermes

  • Meta 专门为 React Native 优化的 JS 引擎
  • AOT 编译:将 JS 源码预编译为 Hermes 字节码(.hbc),应用运行时直接加载字节码
  • 优势:
    • 更快的启动时间:无需运行时解析和编译 JS
    • 更低的内存占用:字节码比 AST 占用更少内存
    • 更小的包体积:字节码通常比压缩后的 JS 源码更小
    • 更好的垃圾回收:使用分代 GC + 增量标记 + 惰性扫描
  • Hermes 的 GC 策略:
    • GenGC(分代垃圾回收):分为 Young Gen 和 Old Gen
    • Young Gen 使用 semi-space copying,Old Gen 使用 mark-compact
    • 支持增量 GC,避免长时间暂停
  • Hermes 不支持的 JS 特性:with 语句、eval() 的动态作用域、部分 Proxy 功能
  • RN 0.70+ 默认在 iOS 和 Android 上启用 Hermes

1.2.3 V8(非官方)

  • 通过 react-native-v8 等第三方库可在 Android 上使用
  • 支持 JIT 编译,运行时性能最高
  • 但启动时间较长,内存占用最高

1.3 React 核心原理(RN 上下文)

1.3.1 虚拟 DOM 与 Reconciliation

  • React 维护一棵虚拟 DOM 树(React Element Tree)
  • 当 state/props 变化时,React 创建新的虚拟 DOM 树
  • 通过 Reconciliation(协调) 算法对比新旧两棵树,找出差异
  • 将差异批量应用到宿主平台(在 RN 中是原生视图)

Reconciliation 核心策略:

  1. 同层比较:只比较同一层级的节点,不跨层级比较(O(n) 复杂度)
  2. 类型判断:如果节点类型不同,直接销毁旧节点及其子树,创建新节点
  3. Key 机制:通过 key 标识列表中的元素,优化列表的增/删/移操作
  4. 组件类型:同类型组件复用实例,不同类型组件重新创建

1.3.2 Fiber 架构

  • React 16+ 引入的新协调引擎
  • 每个 React Element 对应一个 Fiber 节点
  • Fiber 节点是一个链表结构:childsiblingreturn(父节点)
  • 核心特性:
    • 可中断渲染:将渲染工作拆分为多个小单元(unit of work)
    • 优先级调度:不同更新有不同优先级(用户交互 > 数据请求 > 过渡动画)
    • 双缓冲:维护 current tree 和 workInProgress tree,更新完成后交换

Fiber 节点的关键属性:

  • tag:组件类型标识(FunctionComponent, ClassComponent, HostComponent 等)
  • stateNode:指向实际 DOM/Native 节点或类组件实例
  • pendingPropsmemoizedProps:新旧 props
  • memoizedState:当前 state
  • updateQueue:待处理的更新队列
  • effectTag/flags:标记需要执行的副作用(Placement, Update, Deletion)
  • lanes:优先级标记(React 18+)

Fiber 工作流程(两阶段):

  1. Render Phase(可中断)

    • 从根节点开始,深度优先遍历 Fiber 树
    • 对每个 Fiber 节点执行 beginWork(向下遍历)和 completeWork(向上返回)
    • 收集需要执行的副作用(effect list)
    • 此阶段纯计算,不产生可见的 UI 变化
  2. Commit Phase(不可中断)

    • Before Mutation:执行 getSnapshotBeforeUpdate
    • Mutation:执行实际的 DOM/Native 操作(增/删/改)
    • Layout:执行 componentDidMount/componentDidUpdate/useLayoutEffect

1.3.3 Hooks 底层实现

useState 底层:

  • 每个 Hook 在 Fiber 节点上对应一个 Hook 对象,形成链表
  • Hook 对象结构:{ memoizedState, baseState, baseQueue, queue, next }
  • queue 中存放 dispatch 产生的更新(Update 对象链表)
  • 更新时,遍历 queue 中的 Update,依次计算新 state
  • 使用 Object.is 进行浅比较,如果新旧 state 相同则跳过重渲染(bailout)

useEffect 底层:

  • Effect 存储在 Fiber 节点的 updateQueue
  • 每个 Effect 对象:{ tag, create, destroy, deps, next }
  • create:effect 回调函数
  • destroy:上一次 effect 返回的清理函数
  • deps:依赖数组
  • 执行时机:在 Commit Phase 之后,异步调度执行(通过 MessageChannelsetTimeout
  • 对比 useLayoutEffect:在 Commit Phase 的 Layout 子阶段同步执行

useRef 底层:

  • 创建一个 { current: initialValue } 的普通对象
  • 在组件的整个生命周期内保持同一个引用
  • 不会触发重渲染

useMemo / useCallback 底层:

  • 在 Fiber 节点的 Hook 链表中存储 [value, deps][callback, deps]
  • 每次渲染时对比 deps(使用 Object.is 逐项比较)
  • deps 不变则返回缓存值,否则重新计算

useContext 底层:

  • 直接读取最近的 Provider 的 value
  • 当 Provider value 变化时,所有消费该 Context 的组件都会重渲染
  • 不受 React.memoshouldComponentUpdate 等优化手段的影响
  • 这是因为 Context 变化会直接标记消费组件的 Fiber 需要更新

1.3.4 React 18+ Concurrent Features 在 RN 中的应用

  • startTransition:将状态更新标记为低优先级,避免阻塞用户交互
  • useDeferredValue:延迟更新值,让紧急更新先完成
  • Suspense:组件可以 "暂停" 渲染,等待异步数据
  • 在 RN 中,Concurrent Mode 需要 Fabric 渲染器支持
  • Fabric 的 C++ 层支持并发操作 Shadow Tree

1.4 Yoga 布局引擎

1.4.1 核心原理

  • Yoga 是 Meta 开源的跨平台 Flexbox 布局引擎,用 C/C++ 编写
  • 实现了 W3C Flexbox 规范的子集
  • 输入:节点树 + 样式属性(flex, width, height, margin, padding 等)
  • 输出:每个节点的绝对坐标和尺寸(x, y, width, height)

布局计算流程:

  1. 从根节点开始,递归遍历节点树
  2. 对每个节点执行两次 pass:
    • Measure Pass:确定每个节点的固有尺寸(如文本节点需要原生测量)
    • Layout Pass:根据 Flexbox 规则计算最终布局
  3. 计算完成后,将结果传递给原生层进行渲染

1.4.2 RN 样式系统

  • RN 使用 JavaScript 对象描述样式,不支持 CSS
  • StyleSheet.create() 的作用:
    • 在旧架构中:将样式对象注册到 Native,减少 Bridge 传输
    • 在新架构中:主要作用是代码层面的优化(对象引用缓存)
    • 提供开发时的样式验证
  • RN 的样式属性名使用驼峰命名(camelCase),对应 CSS 的 kebab-case

1.4.3 RN 中不支持的 CSS 特性

  • 不支持 CSS Grid(需要第三方库)
  • 不支持 CSS 选择器、伪类、伪元素
  • 不支持 CSS 变量
  • 不支持 display: inline(所有元素默认 display: flex
  • 不支持 float
  • 不支持 overflow: scroll(需要使用 ScrollView)
  • 不支持百分比 padding/margin 参照父元素宽度(Yoga 限制)

1.5 线程模型

1.5.1 旧架构线程模型

线程职责
JS Thread执行 JavaScript 代码、React 渲染逻辑、业务逻辑
Main/UI Thread原生视图操作、触摸事件处理、系统回调
Shadow ThreadYoga 布局计算
Native Modules Thread部分 Native Module 的异步操作

1.5.2 新架构线程模型

  • Fabric 允许在任意线程执行渲染操作
  • 布局计算可以在后台线程异步进行
  • 支持同步渲染路径(用于紧急更新)
  • JS 线程可以同步读取布局信息(通过 JSI)

1.6 事件系统

1.6.1 旧架构事件流

  1. 用户触摸屏幕 → 原生系统捕获事件
  2. 原生事件处理器将事件序列化为 JSON
  3. 通过 Bridge 发送到 JS 线程
  4. JS 线程中 React 的合成事件系统处理事件
  5. 如果需要更新 UI,JS 发送更新指令通过 Bridge 返回
  6. 原生线程执行 UI 更新

问题:由于 Bridge 异步通信,事件处理存在延迟,导致:

  • 手势响应不及时
  • 滑动列表时的白屏问题
  • 动画卡顿

1.6.2 新架构事件流

  1. 通过 JSI 直接传递事件,无序列化开销
  2. 支持同步事件处理
  3. 可以在 C++ 层直接拦截和处理部分事件

1.6.3 手势响应系统

PanResponder / Gesture Responder System:

  • React Native 内置的手势系统
  • 基于 "协商" 机制:多个组件可以竞争成为事件的 "responder"
  • 生命周期:onStartShouldSetResponderonMoveShouldSetResponderonResponderGrantonResponderMoveonResponderRelease
  • 问题:全部在 JS 线程处理,手势复杂时性能差

react-native-gesture-handler:

  • 手势处理完全在原生线程执行
  • 支持手势组合、手势竞争
  • 与 react-native-reanimated 配合实现高性能手势动画

1.7 导航系统(Navigation)

1.7.1 React Navigation

  • 纯 JS 实现的导航库
  • 使用 React 状态管理导航状态
  • 页面切换动画可以通过 react-native-reanimated 在 UI 线程执行
  • 支持 Stack、Tab、Drawer 等导航模式
  • 导航状态可序列化,支持 Deep Link 和状态持久化

1.7.2 react-native-screens

  • 使用原生的页面容器(iOS: UIViewController, Android: Fragment)
  • 减少内存占用(被遮挡的页面可以被系统回收)
  • 与 React Navigation 配合使用

1.8 热更新原理

1.8.1 CodePush 原理

  1. 将 JS Bundle 和资源文件上传到 CodePush 服务器
  2. 应用启动时检查更新
  3. 下载新的 JS Bundle(差量更新,使用 diff 算法)
  4. 应用新的 Bundle:
    • 立即重启InstallMode.IMMEDIATE
    • 下次启动InstallMode.ON_NEXT_RESTART
    • 后台恢复InstallMode.ON_NEXT_RESUME
  5. 回滚机制:如果新 Bundle 导致 crash,自动回滚到上一版本

1.8.2 热更新的限制

  • 只能更新 JS Bundle 和资源文件
  • 不能更新原生代码(Objective-C/Swift/Java/Kotlin)
  • 不能添加或修改原生模块
  • 不能修改 App 的权限配置

1.9 原生模块(Native Modules)

1.9.1 旧架构 NativeModules

iOS (Objective-C):

  1. 创建类实现 RCTBridgeModule 协议
  2. 使用 RCT_EXPORT_MODULE() 宏注册模块
  3. 使用 RCT_EXPORT_METHOD() 宏导出方法
  4. JS 端通过 NativeModules.ModuleName 访问

数据传递:

  • 基本类型:number, string, boolean
  • 复杂类型:array, object(经过 JSON 序列化)
  • 回调:RCTResponseSenderBlock
  • Promise:RCTPromiseResolveBlock + RCTPromiseRejectBlock

1.9.2 新架构 TurboModules

  • 使用 Flow/TypeScript 类型定义接口(Spec 文件)
  • Codegen 自动生成 C++、ObjC、Java 胶水代码
  • 基于 JSI,JS 直接持有 Native 对象引用
  • 支持同步方法调用
  • 懒加载:首次访问时才创建实例

1.9.3 原生 UI 组件

旧架构(ViewManager):

  1. iOS: 继承 RCTViewManager,实现 -(UIView *)view 方法
  2. 使用 RCT_EXPORT_VIEW_PROPERTY 导出属性
  3. JS 端通过 requireNativeComponent 注册

新架构(Fabric Component):

  1. 定义 TypeScript Spec(组件接口)
  2. Codegen 生成 C++ Shadow Node 和 Component Descriptor
  3. 实现原生视图和 Shadow Node
  4. 直接通过 JSI 通信,无 Bridge 开销

1.10 Metro Bundler

1.10.1 打包流程

  1. Resolution(解析):从入口文件开始,递归解析所有 import/require 依赖
  2. Transformation(转换):使用 Babel 将 JSX/TS/Flow 转换为标准 JS
  3. Serialization(序列化):将所有模块打包成单一 Bundle 文件

1.10.2 模块系统

  • Metro 将每个模块包装在一个工厂函数中
  • 使用数字 ID 标识模块(而非路径字符串,减少包体积)
  • __r() 函数用于 require 模块
  • __d() 函数用于定义模块

1.10.3 RAM Bundle(Random Access Module Bundle)

  • 将 Bundle 拆分为多个模块
  • 应用启动时只加载必要的模块
  • 其他模块在首次 require 时按需加载
  • iOS 使用 Indexed RAM Bundle 格式
  • Android 使用 File RAM Bundle 格式(每个模块一个文件)

1.11 组件生命周期

1.11.1 类组件生命周期

挂载阶段:

  • constructor()static getDerivedStateFromProps()render()componentDidMount()

更新阶段:

  • static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()

卸载阶段:

  • componentWillUnmount()

错误处理:

  • static getDerivedStateFromError()componentDidCatch()

1.11.2 函数组件 Hooks 对应关系

类组件生命周期Hooks 等价
constructoruseState 初始值 / useRef
getDerivedStateFromProps在渲染期间根据 props 更新 state
shouldComponentUpdateReact.memo
render函数组件本身
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {}, [deps])
componentWillUnmountuseEffect 的返回清理函数
componentDidCatch无直接对应(需 Error Boundary 类组件)
getSnapshotBeforeUpdate无直接对应

1.12 状态管理

1.12.1 React 内置状态管理

useState:

  • 组件级别的状态
  • 触发组件及其子组件的重渲染
  • 更新是异步批量的(React 18 自动批处理所有更新)

useReducer:

  • 适合复杂状态逻辑
  • 类似 Redux 的 dispatch/action 模式
  • 与 useContext 配合可实现简易全局状态管理

Context API:

  • 跨组件层级传递数据
  • Provider/Consumer 模式
  • 性能问题:value 变化会导致所有消费者重渲染,无法精确更新

1.12.2 外部状态管理方案

Redux:

  • 单一数据源(Single Store)
  • 纯函数 Reducer
  • 通过 dispatch(action) 更新状态
  • 中间件系统(redux-thunk, redux-saga)
  • 适用于大型应用

MobX:

  • 基于可观察的响应式编程
  • observablecomputedreaction 的数据流
  • 自动追踪依赖,精确更新
  • 代码更简洁,但调试较困难

Zustand:

  • 极简的状态管理库
  • 基于发布-订阅模式
  • 通过 selector 实现精确订阅(避免不必要的重渲染)
  • 不依赖 Context,避免 Provider 嵌套

Jotai:

  • 原子化的状态管理
  • 每个 atom 是最小状态单元
  • 自底向上的状态组合
  • 基于 WeakMap 存储,自动垃圾回收

Recoil(Meta 出品):

  • atom + selector 模式
  • 支持异步 selector
  • 与 React Concurrent Mode 深度集成

第二部分:第三方常用库原理与八股文

2.1 react-native-reanimated

2.1.1 核心原理

  • 动画逻辑完全在 UI 线程 执行,不经过 JS 线程
  • 使用 worklets(小型 JS 函数)在 UI 线程运行
  • Babel 插件在编译时将 worklet 函数提取出来,序列化后传递给原生端
  • 原生端使用独立的 JS 运行时(基于 JSI)执行 worklet
  • 通过 Shared Values 在 JS 线程和 UI 线程之间共享数据

2.1.2 Shared Values

  • useSharedValue() 创建一个可以在 JS 线程和 UI 线程之间共享的值
  • 修改 .value 时,变化会同步到 UI 线程
  • 不会触发 React 重渲染
  • 底层通过 JSI 的 Host Object 实现,两个线程持有同一个 C++ 对象的引用

2.1.3 Worklets

  • 使用 'worklet' 标记的函数会在编译时被提取
  • Babel 插件将 worklet 函数及其闭包变量序列化
  • 运行时在 UI 线程的独立 JS 上下文中执行
  • 通过 runOnJS() 可以从 worklet 中回调 JS 线程的函数
  • 通过 runOnUI() 可以从 JS 线程调用 UI 线程的 worklet

2.1.4 动画 API

  • withTiming() / withSpring() / withDecay():基础动画
  • useAnimatedStyle():根据 Shared Value 生成动画样式(在 UI 线程执行)
  • useAnimatedGestureHandler():与 gesture-handler 配合,在 UI 线程处理手势
  • Layout Animations:进入/退出/布局变化动画

2.1.5 与 Animated API 的本质区别

特性Animated(内置)Reanimated 2/3
动画执行线程JS 线程(useNativeDriver=true 时部分在 UI 线程)完全在 UI 线程
手势联动受限完美支持
条件动画不支持(声明式)支持 worklet 中的逻辑判断
基于其他动画的动画受限自由组合
性能中等最优
学习成本中高

2.2 react-native-gesture-handler

2.2.1 核心原理

  • 将手势处理从 JS 线程移到原生线程
  • iOS 使用 UIGestureRecognizer
  • Android 使用自定义的手势处理系统
  • 支持手势的声明式组合和竞争

2.2.2 手势类型

  • Tap:点击手势
  • Pan:拖动手势
  • Pinch:缩放手势
  • Rotation:旋转手势
  • Fling:快速滑动手势
  • LongPress:长按手势
  • NativeViewGestureHandler:桥接原生手势

2.2.3 手势竞争与组合

  • Race:多个手势竞争,第一个激活的获胜
  • Simultaneous:多个手势同时识别
  • Exclusive:互斥手势

2.2.4 与 Reanimated 配合

  • Gesture Handler v2 的 useAnimatedGestureHandler 直接在 UI 线程回调
  • 手势数据(translation, velocity 等)直接在 UI 线程更新 Shared Values
  • 实现 60fps 手势驱动动画

2.3 React Navigation

2.3.1 架构设计

  • NavigationContainer:根组件,管理导航状态
  • Navigator:导航器(Stack, Tab, Drawer),定义导航模式
  • Screen:路由配置

2.3.2 状态管理

  • 导航状态是一棵 JSON 树
  • 每个 Navigator 管理自己的子路由状态
  • 状态变化通过 dispatch(action) 驱动
  • 支持状态持久化(onStateChange + initialState

2.3.3 页面生命周期

  • useFocusEffect:页面获得/失去焦点时执行(类似页面可见性变化)
  • useIsFocused:当前页面是否聚焦
  • 注意:Stack Navigator 中非当前页面不会卸载,仍然保持在内存中

2.3.4 Deep Linking

  • 将 URL 路径映射到路由配置
  • 支持 Universal Links (iOS) 和 App Links (Android)
  • linking 配置定义 URL 到路由的映射规则

2.4 Redux & 中间件

2.4.1 Redux 核心流程

Action → Dispatch → Middleware → Reducer → Store → UI

2.4.2 Redux Thunk 原理

  • 最简单的异步中间件(只有 14 行代码)
  • 核心逻辑:如果 dispatch 的 action 是函数,就执行该函数并传入 dispatch 和 getState
  • 适合简单的异步场景

2.4.3 Redux Saga 原理

  • 基于 ES6 Generator 实现
  • Saga 是一个 Generator 函数,yield 出 Effect 描述对象
  • Saga 中间件解释 Effect 并执行相应操作
  • 常用 Effects:call(调用异步函数)、put(dispatch action)、take(监听 action)、fork(非阻塞调用)、select(获取 state)
  • 优势:可测试性强、复杂异步流程管理、取消任务
  • 劣势:学习成本高、代码量大

2.4.4 Redux Toolkit (RTK)

  • 官方推荐的 Redux 使用方式
  • createSlice:自动生成 action creators 和 reducer
  • createAsyncThunk:标准化异步操作
  • 内置 Immer(可以直接 "修改" state,Immer 内部通过 Proxy 实现不可变更新)
  • RTK Query:内置的数据获取和缓存方案

2.5 网络请求库

2.5.1 Axios 在 RN 中的使用

  • 底层在 RN 中使用 XMLHttpRequest polyfill
  • 支持请求/响应拦截器
  • 自动 JSON 序列化/反序列化
  • 支持取消请求(AbortController / CancelToken)

2.5.2 React Query / TanStack Query

  • 服务端状态管理库
  • 核心概念:Query、Mutation、Query Invalidation
  • 自动缓存、后台刷新、过期重新获取
  • 支持乐观更新
  • 与 RN 配合:通过 focusManageronlineManager 适配 RN 的生命周期

2.6 存储库

2.6.1 AsyncStorage

  • 简单的 key-value 存储
  • iOS 底层使用 serialized dictionary / SQLite
  • Android 底层使用 SQLite / SharedPreferences
  • 异步 API(基于 Promise)
  • 不适合大量数据存储(性能差)

2.6.2 MMKV(react-native-mmkv)

  • 微信开发的高性能 key-value 存储
  • 基于 mmap 内存映射文件
  • 同步 API(通过 JSI 直接调用 C++ 方法)
  • 比 AsyncStorage 快 30x
  • 支持加密存储
  • 支持多进程访问

2.6.3 WatermelonDB

  • 基于 SQLite 的响应式数据库
  • 懒加载:只在需要时从数据库加载数据
  • 观察者模式:数据变化自动触发 UI 更新
  • 支持同步到远程服务器
  • 适合大量结构化数据

2.6.4 Realm

  • 对象数据库(非关系型)
  • 零拷贝架构:直接操作 mmap 映射的数据
  • 支持实时查询
  • 支持跨平台数据同步(MongoDB Realm Sync)

2.7 react-native-svg

  • 使用原生 SVG 渲染(iOS: CoreGraphics, Android: Android Canvas)
  • 通过 ViewManager 将 SVG 元素映射到原生视图
  • 支持动画(配合 Reanimated)
  • 支持渐变、阴影、裁剪路径等

2.8 react-native-fast-image

  • 替代 RN 内置 Image 组件
  • iOS 底层使用 SDWebImage
  • Android 底层使用 Glide
  • 支持多级缓存(内存 → 磁盘)
  • 支持优先级加载
  • 支持渐进式加载
  • 支持预加载

2.9 react-native-webview

  • 封装原生 WebView(iOS: WKWebView, Android: Android WebView / Chromium)
  • JS ↔ WebView 通信:
    • RN → WebView:injectJavaScript() / postMessage()
    • WebView → RN:window.ReactNativeWebView.postMessage()
  • 支持离线加载本地 HTML
  • 安全考虑:可以控制 JS 注入、cookie、导航策略

2.10 Expo

2.10.1 Expo 架构

  • Expo Go:开发期间的容器 App,内置常用原生模块
  • Expo SDK:一系列预构建的原生模块(Camera, FileSystem, Location 等)
  • EAS (Expo Application Services):云端构建、提交、更新服务
  • Expo Modules API:编写自定义原生模块的现代 API(基于 Swift/Kotlin DSL)
  • Expo Router:基于文件系统的路由(类似 Next.js)

2.10.2 Managed vs Bare Workflow

特性ManagedBare
原生代码访问不可直接访问完全控制
构建EAS Build本地 Xcode/Android Studio
原生模块仅 Expo SDK任意第三方库
配置app.json/app.config.js原生项目配置
Eject可以 prebuildN/A

第三部分:RN 开发难点与解决方案

3.1 长列表性能问题

3.1.1 问题描述

  • FlatList 在数据量大时滑动卡顿、掉帧
  • 快速滑动出现白屏/空白区域
  • 内存持续增长,大量列表项导致 OOM

3.1.2 原因分析

  • FlatList 的回收机制是 JS 层面的(而非原生的 RecyclerView/UICollectionView)
  • 每次渲染新的列表项都需要经过 JS 线程 → Bridge/JSI → Native 的完整流程
  • 复杂列表项的 React 组件 diffing 开销大
  • 图片加载、数据处理等操作堆积在 JS 线程

3.1.3 解决方案

FlatList 优化:

  • getItemLayout:提供固定高度,跳过动态测量(避免异步布局计算)
  • removeClippedSubviews={true}:移除可视区域外的子视图(iOS 效果好,Android 可能有问题)
  • maxToRenderPerBatch:控制每批渲染的数量(默认 10,减少可降低单帧渲染压力)
  • windowSize:控制渲染窗口大小(默认 21,即可视区域上下各 10 屏。减小可降低内存,但增加白屏风险)
  • updateCellsBatchingPeriod:批量更新的间隔时间
  • initialNumToRender:首屏渲染数量
  • 避免匿名函数:renderItem 和事件处理器使用 useCallback 缓存
  • 避免内联样式:使用 StyleSheet.createuseMemo 缓存样式对象
  • keyExtractor:提供稳定唯一的 key

使用 FlashList(@shopify/flash-list)替代 FlatList:

  • 使用 RecyclerListView 内核,实现真正的视图回收
  • 不再销毁/创建新组件,而是复用已有组件实例
  • 估算列表项尺寸,减少布局抖动
  • 性能比 FlatList 提升 5-10 倍
  • 需要正确设置 estimatedItemSize

使用原生列表(高度定制场景):

  • 直接使用 iOS UICollectionView / Android RecyclerView
  • 通过 Fabric Component 暴露给 JS

3.2 键盘遮挡输入框

3.2.1 问题描述

  • 键盘弹出时遮挡输入框
  • 不同设备键盘高度不同
  • Android 和 iOS 行为差异大
  • ScrollView 内的输入框定位困难

3.2.2 解决方案

内置方案:

  • KeyboardAvoidingView:根据键盘高度调整内容位置
    • behavior 属性:iOS 通常用 "padding",Android 用 "height" 或不设置
    • keyboardVerticalOffset:用于补偿导航栏等固定元素的高度
  • ScrollViewkeyboardShouldPersistTaps:控制点击行为
  • TextInputscrollEnabled / onFocus 手动滚动

第三方方案:

  • react-native-keyboard-aware-scroll-view:自动滚动到聚焦的输入框
  • react-native-avoid-softinput:原生实现的键盘避让
  • Android windowSoftInputMode 配置:adjustResize / adjustPan

3.2.3 深入细节

  • iOS 可以监听 keyboardWillShow(动画开始前),Android 只有 keyboardDidShow(已显示后)
  • 这导致 Android 上键盘避让动画不够流畅
  • 解决方案:Android 使用 WindowInsetsAnimation(API 30+)或 react-native-keyboard-controller

3.3 图片相关问题

3.3.1 大图片内存溢出

  • 高分辨率图片加载到内存中占用巨大空间(4000x3000 的图片 ≈ 48MB 原始内存)
  • 多张大图同时显示导致 OOM crash

解决方案:

  • 使用 react-native-fast-image 配合合理的缓存策略
  • 服务端提供多分辨率图片(CDN 图片处理)
  • 使用 resizeModeresizeMethod(Android)控制图片解码大小
  • iOS: 使用 Image.resolveAssetSource() 获取实际尺寸后按比例缩放
  • 列表中图片使用懒加载(FlatList 的 viewport 机制)

3.3.2 图片缓存策略

  • RN 内置 Image 组件的缓存行为在 iOS 和 Android 上不一致
  • iOS 使用 NSURLCache(HTTP 缓存),Android 使用 OkHttp 缓存
  • 推荐使用 react-native-fast-image 统一缓存策略

3.4 启动白屏 / 启动性能

3.4.1 问题描述

  • App 启动后出现短暂白屏
  • JS Bundle 加载和执行时间长
  • 首屏渲染慢

3.4.2 原因分析

启动流程:

  1. 原生应用初始化
  2. JS 引擎初始化
  3. 加载 JS Bundle
  4. 执行 JS Bundle(所有全局代码)
  5. React 初始化,首次渲染
  6. 布局计算
  7. 原生视图创建和渲染

每一步都可能成为瓶颈。

3.4.3 解决方案

  • 使用 Hermes:预编译字节码,跳过 JS 解析和编译步骤
  • 启动屏 (Splash Screen):使用 react-native-splash-screen / expo-splash-screen,在原生层显示启动图,JS 加载完成后隐藏
  • 减少 Bundle 体积:Tree Shaking、代码分割、移除无用依赖
  • 延迟加载:非首屏需要的模块使用 require() 延迟加载(而非顶层 import
  • Inline Requires:Metro 配置 inlineRequires: true,将模块加载延迟到首次使用
  • RAM Bundle:将模块按需加载
  • 减少全局初始化代码:避免在模块顶层执行耗时操作
  • 原生模块懒加载(TurboModules):只在需要时初始化原生模块

3.5 平台差异适配

3.5.1 常见差异

差异项iOSAndroid
阴影shadowColor/Offset/Opacity/Radiuselevation(不支持精细控制)
字体SF Pro (系统)Roboto (系统)
StatusBar默认不透明默认透明/半透明
返回按钮无物理返回键有物理/虚拟返回键
触摸反馈无 rippleripple 效果
输入法可控行为多样(厂商定制)
权限Info.plist 声明AndroidManifest + 运行时申请
通知APNsFCM
WebViewWKWebViewChromium

3.5.2 适配方案

  • Platform.OS:运行时判断平台
  • Platform.select():根据平台返回不同值
  • 文件后缀:Component.ios.tsx / Component.android.tsx
  • Platform.Version:获取系统版本号
  • Dimensions / useWindowDimensions:获取屏幕尺寸
  • 安全区域:react-native-safe-area-context

3.6 复杂动画实现

3.6.1 问题

  • 复杂的手势驱动动画在 JS 线程执行会卡顿
  • 多个动画组合和串联的控制复杂
  • 布局动画(LayoutAnimation)在 Android 上的兼容性问题

3.6.2 解决方案

  • react-native-reanimated + gesture-handler:将动画和手势全部移到 UI 线程
  • LayoutAnimation(简单场景):一行代码实现布局变化动画
    • iOS 支持良好
    • Android 需要在 MainApplication 中开启:UIManager.setLayoutAnimationEnabledExperimental(true)
  • Lottie (react-native-lottie):播放 After Effects 导出的动画文件
    • 原生渲染,性能好
    • 适合复杂的设计动画

3.7 内存泄漏

3.7.1 常见内存泄漏场景

  1. 未清理的定时器setInterval/setTimeout 没有在组件卸载时清除
  2. 未取消的订阅:事件监听、数据订阅没有取消
  3. 未取消的网络请求:组件卸载后请求回调中仍更新 state
  4. 闭包引用:useEffect/useCallback 中的闭包持有大对象引用
  5. 全局变量累积:模块级变量持续增长
  6. 图片缓存过大:大量图片缓存未清理
  7. 导航栈过深:Stack Navigator 中页面不断 push 但不 pop

3.7.2 排查方法

  • Xcode Instruments → Allocations/Leaks:查看内存分配和泄漏
  • Android Studio Profiler → Memory:查看堆内存、分配追踪
  • Hermes 采样 profiler:分析 JS 内存使用
  • Flipper + LeakCanary (Android):自动检测内存泄漏
  • Chrome DevTools → Memory 快照:对比不同时间点的内存快照
  • __DEV__ 模式下的 Warning:React 会警告卸载组件上的 state 更新

3.7.3 解决方案

  • useEffect 中必须返回清理函数
  • 使用 AbortController 取消网络请求
  • 使用 useRef 标记组件挂载状态(isMounted.current
  • 合理使用导航:replace 代替 push,定期 reset 导航栈
  • 大对象使用后主动置 null
  • 图片缓存设置最大容量

3.8 多语言 / 国际化

3.8.1 技术方案

  • react-native-localize:获取设备语言设置
  • react-i18next / i18next:国际化框架
    • 支持命名空间
    • 支持变量插值
    • 支持复数规则
    • 支持语言回退

3.8.2 RTL(从右到左)布局

  • 阿拉伯语、希伯来语等需要 RTL 布局
  • I18nManager.forceRTL(true) 强制 RTL
  • Flexbox 的 flexDirection: 'row' 在 RTL 下自动反转
  • 需要注意图标、动画方向的适配

3.9 Debugging 痛点

3.9.1 常见问题

  • Red Screen of Death (RSOD):JS 运行时错误
  • Yellow Box:警告信息
  • 调试时性能与生产环境差异大(DEV 模式下有大量检查)
  • Remote Debugger(Chrome)下行为与真机不一致(使用 Chrome V8 而非 JSC/Hermes)
  • Source Map 不准确导致断点位置偏移

3.9.2 调试工具

  • Flipper:Meta 官方调试工具

    • 网络请求查看
    • Layout Inspector
    • React DevTools 集成
    • 数据库查看
    • 原生日志查看
    • 自定义插件
  • React DevTools:查看组件树、props、state

  • Hermes Debugger:直接在 Hermes 引擎上调试(通过 Chrome DevTools Protocol)

    • 比 Remote Debugger 更准确(不切换 JS 引擎)
    • 支持断点、变量查看、性能分析
  • console.log + Metro 日志:最基础的调试方式

3.10 安装与构建问题

3.10.1 常见构建问题

  • CocoaPods 版本冲突:多个库依赖不同版本的同一原生库
    • 解决:pod install --repo-update,或在 Podfile 中手动指定版本
  • Android Gradle 构建失败
    • 依赖版本冲突:resolutionStrategy
    • NDK 版本不匹配:统一 NDK 版本
    • 内存不足:增加 Gradle JVM 内存 -Xmx4g
  • Xcode 签名问题:证书、Provisioning Profile 配置
  • 新架构迁移:旧的 NativeModules 不兼容 TurboModules
    • 使用 Interop Layer 过渡
    • 逐步迁移到 Codegen 接口

3.10.2 依赖管理

  • react-native-config:管理不同环境(dev/staging/production)的配置
  • patch-package:修补第三方库的 bug(不等上游发版)
  • auto-linking:RN 0.60+ 自动链接原生依赖
  • pod-install:自动化 CocoaPods 安装

3.11 OTA 热更新的坑

3.11.1 常见问题

  • 更新包过大导致下载失败
  • 更新后崩溃且无法回滚
  • 多版本共存的兼容性
  • 审核合规问题(Apple 对热更新的限制)

3.11.2 解决方案

  • 差量更新减少包大小
  • 实现回滚机制(CodePush 内置)
  • 灰度发布(按百分比/用户群推送)
  • 版本号管理:targetBinaryVersion 匹配原生版本
  • Apple 要求:热更新不能改变 App 的主要用途和功能

3.12 大型项目的代码组织

3.12.1 Monorepo 方案

  • 使用 Yarn Workspaces / Turborepo / Nx
  • 共享代码(UI 组件、工具函数、类型定义)
  • 独立的 App 包和库包
  • 统一的依赖版本管理

3.12.2 模块化架构

  • 按功能域划分模块(Feature-based)
  • 每个模块独立的路由、状态、API
  • 模块间通过定义好的接口通信
  • 支持模块的独立开发和测试

第四部分:性能优化八股文与细节

4.1 渲染性能优化

4.1.1 避免不必要的重渲染

React.memo:

  • 对函数组件的 props 进行浅比较(shallow compare)
  • 如果 props 未变化,跳过重渲染
  • 浅比较规则:基本类型用 ===,引用类型比较引用地址
  • 可以传入自定义比较函数 arePropsEqual
  • 注意:如果组件本身很简单,React.memo 的比较开销可能超过重渲染开销

useMemo:

  • 缓存计算结果
  • 依赖项不变时返回缓存值
  • 适用于:昂贵的计算、创建新对象/数组(避免引用变化触发子组件重渲染)
  • 注意:useMemo 本身有开销(存储缓存值 + 比较依赖项),简单计算不需要 useMemo

useCallback:

  • 缓存函数引用
  • 本质是 useMemo(() => fn, deps) 的语法糖
  • 主要用于:传递给 React.memo 包裹的子组件的回调函数
  • 如果不配合 React.memo 使用,useCallback 几乎没有优化效果

重渲染的触发条件:

  1. 自身 state 变化
  2. 父组件重渲染(即使 props 未变化,除非使用 React.memo
  3. Context value 变化(消费了该 Context 的组件)
  4. 强制更新(forceUpdate

常见的性能陷阱:

// 陷阱 1:内联对象/数组 → 每次渲染创建新引用
<View style={{ flex: 1 }} />  // 每次渲染都是新对象
// 解决:使用 StyleSheet.create 或 useMemo

// 陷阱 2:内联函数 → 每次渲染创建新函数
<Button onPress={() => doSomething()} />
// 解决:使用 useCallback(配合 React.memo 的子组件才有意义)

// 陷阱 3:Context 传递新对象
<Context.Provider value={{ theme, setTheme }}>
// 每次渲染 value 都是新对象 → 所有消费者重渲染
// 解决:使用 useMemo 缓存 value

4.1.2 列表渲染优化

FlatList 性能参数详解:

参数默认值作用调优建议
windowSize21渲染窗口(屏幕数)减小可降低内存,但增加白屏风险
maxToRenderPerBatch10每批渲染的最大数量减小可降低单帧渲染压力
updateCellsBatchingPeriod50ms批量更新间隔增大可减少渲染频率
initialNumToRender10首屏渲染数量设置为首屏可见的数量
removeClippedSubviewsfalse移除不可见视图iOS 上启用,Android 慎用

FlashList 优化原理:

  • 真正的视图回收:组件实例被复用,只更新 props
  • Cell 类型系统:相同类型的 Cell 才会互相回收复用
  • overrideItemLayout:精确控制每个 item 的尺寸
  • estimatedItemSize:估算平均尺寸,减少布局跳动
  • prepareForRecycling:组件被回收时清理状态

4.1.3 图片渲染优化

  • 使用正确的图片尺寸(匹配显示尺寸 × 像素密度)
  • 使用 WebP 格式(体积更小)
  • 实现渐进式加载(模糊预览 → 清晰图片)
  • 使用 react-native-fast-image 的缓存策略
  • 列表中图片使用固定宽高(避免布局跳动)
  • 考虑使用 react-native-blurhash 做占位图

4.1.4 组件拆分策略

  • 大组件拆分:将大组件拆分为小组件,state 变化只影响小组件重渲染
  • 状态下沉:将 state 放到最需要它的组件中(不要提升到不必要的高层)
  • 内容提升(Lifting Content Up):将不变的 JSX 作为 children 传入,避免重渲染
  • 渲染分离:将频繁更新的部分(如动画值显示)与静态部分分离

4.2 JS 线程性能优化

4.2.1 减少 JS 线程工作量

  • 复杂计算使用 InteractionManager.runAfterInteractions() 延迟执行
  • 使用 requestAnimationFrame 分批处理
  • 动画使用 useNativeDriver: true(Animated API)或使用 Reanimated
  • 大数据处理考虑使用 Web Worker(通过 react-native-worker-threads

4.2.2 避免 JS 线程阻塞

  • 避免在渲染中进行同步的大计算
  • JSON 解析大数据时使用流式解析或分批解析
  • 正则表达式避免回溯灾难(Catastrophic Backtracking)
  • 避免深层对象的深拷贝

4.2.3 InteractionManager 详解

  • runAfterInteractions:在所有动画和交互完成后执行回调
  • 原理:维护一个交互任务队列,当没有正在进行的动画/触摸时,执行排队的任务
  • 适用场景:页面跳转后加载数据、动画完成后执行计算
  • 可以返回 { cancel } 用于取消排队的任务

4.3 内存优化

4.3.1 内存使用模式

  • JS Heap:JS 对象占用的内存
  • Native Heap:原生视图、图片等占用的内存
  • Graphics Memory:GPU 纹理占用的内存

4.3.2 具体优化策略

JS 层面:

  • 避免创建不必要的对象(对象池模式)
  • 大数组使用分页加载
  • 及时解除大对象的引用
  • 使用 WeakRef / WeakMap 管理缓存
  • 避免闭包持有大对象

图片层面:

  • 设置图片缓存上限
  • 及时清理不需要的图片缓存(Image.queryCache() + clearCache()
  • 使用合适分辨率的图片
  • 列表中的图片在滑出视口后释放

原生视图层面:

  • removeClippedSubviews:移除不可见视图的原生实例
  • react-native-screens:被遮挡的页面可以被系统回收
  • 避免深层嵌套的视图层级

4.3.3 内存监控

  • iOS:Xcode Memory Graph Debugger
  • Android:Android Studio Memory Profiler
  • 自定义内存监控:通过原生模块定期报告内存使用
  • Hermes:使用 HermesInternal.getInstrumentedStats() 获取内存统计

4.4 启动性能优化(深入)

4.4.1 启动时间分解

冷启动流程(毫秒级分解):

  1. 进程创建(~50-200ms):操作系统创建进程
  2. 原生初始化(~100-500ms):
    • Application 创建
    • 原生模块注册和初始化(旧架构:全量初始化)
    • JS 引擎初始化
  3. JS Bundle 加载(~50-500ms):
    • 文件读取
    • Hermes 字节码加载 / JSC 源码解析
  4. JS 执行(~100-1000ms):
    • 全局代码执行
    • 模块初始化
    • React 组件树构建
  5. 首次渲染(~50-200ms):
    • Yoga 布局计算
    • 原生视图创建
  6. 可交互(TTI):用户可以操作的时间点

4.4.2 优化策略详解

Bundle 体积优化:

  • 使用 metro.config.jstransformer.minifierConfig 优化压缩
  • console.log 在生产环境移除:使用 babel-plugin-transform-remove-console
  • 分析 Bundle 组成:使用 react-native-bundle-visualizer
  • 移除未使用的导出:Tree Shaking(Metro 对此支持有限,需关注)
  • 图片资源优化:使用合适的格式和压缩

Inline Requires 详解:

// 关闭 inline requires 时
import { HeavyModule } from './HeavyModule'; // 应用启动时立即执行

// 开启 inline requires 后(Metro 自动转换)
// import 被转换为 lazy require
let _HeavyModule;
function getHeavyModule() {
  if (!_HeavyModule) {
    _HeavyModule = require('./HeavyModule');
  }
  return _HeavyModule;
}
  • Metro 配置:transformer: { inlineRequires: true }
  • 效果:模块在首次使用时才加载,减少启动时的初始化工作
  • 注意:有些模块有副作用(side effects),不适合延迟加载

并行初始化:

  • 原生初始化和 JS 加载并行执行
  • 显示 Splash Screen 时在后台预加载数据
  • 使用 InteractionManager 延迟非关键初始化

4.4.3 启动性能测量

  • iOS:Xcode Instruments → Time Profiler
  • Androidadb shell am start -W 测量启动时间
  • React Nativeperformance.now()Date.now() 打点
  • Hermes:内置的采样 profiler(HermesInternal.enableSamplingProfiler()
  • Flipper:查看启动阶段的各种指标

4.5 网络性能优化

4.5.1 请求优化

  • 请求合并:使用 GraphQL 减少请求次数
  • 请求缓存:React Query / SWR 的缓存策略
  • 请求优先级:关键数据先加载,非关键数据延迟加载
  • 取消无效请求:页面离开时取消进行中的请求
  • 压缩:Gzip/Brotli 压缩请求和响应
  • 连接复用:HTTP/2 多路复用

4.5.2 离线支持

  • 本地数据缓存(MMKV / SQLite / WatermelonDB)
  • 乐观更新:先更新 UI,后发送请求
  • 离线队列:记录离线操作,恢复网络后批量发送
  • NetInfo 监听网络状态变化

4.6 动画性能优化

4.6.1 60fps 的基本要求

  • 每帧预算:1000ms / 60 = 16.67ms
  • JS 线程和 UI 线程都需要在 16.67ms 内完成工作
  • 任何线程超过预算都会导致掉帧

4.6.2 Animated API 性能技巧

  • useNativeDriver: true
    • 将动画序列序列化后发送到原生端
    • 动画在 UI 线程执行,不经过 JS 线程
    • 限制:只支持非布局属性(transform, opacity),不支持 width/height/margin 等
    • 原理:将动画描述(起始值、目标值、缓动函数、持续时间)一次性发送给原生端
  • 避免在动画过程中更新 state
  • 使用 Animated.eventuseNativeDriver 处理滑动联动

4.6.3 Reanimated 性能最佳实践

  • 避免在 worklet 中使用 runOnJS(会产生跨线程通信)
  • Shared Values 使用基本类型(number, string, boolean),避免复杂对象
  • useAnimatedStyle 中只依赖 Shared Values,不依赖 React state
  • 合理使用 cancelAnimation 避免动画堆积
  • 利用 withRepeatwithSequence 等高级 API 减少 worklet 复杂度

4.7 包体积优化

4.7.1 分析工具

  • react-native-bundle-visualizer:可视化 Bundle 组成
  • source-map-explorer:分析 Source Map
  • npx react-native-asset-bundle-size:分析资源文件大小

4.7.2 优化策略

  • 移除未使用的依赖(depcheck
  • 使用更小的替代库(moment.js → dayjs, lodash → lodash-es 或单独导入)
  • 图片资源压缩(TinyPNG、WebP)
  • 字体文件精简(只包含使用到的字符子集)
  • ProGuard (Android) 代码混淆和压缩
  • Bitcode (iOS) 优化
  • Hermes 字节码通常比 JS 源码更小

4.7.3 代码分割

  • React.lazy + Suspense(Web 上有效,RN 中有限支持)
  • 手动代码分割:使用 require() 动态加载
  • Metro 的 serializer.customSerializer 自定义分割逻辑

4.8 持续性能监控

4.8.1 开发时监控

  • Performance Monitor(RN 内置)

    • JS Thread FPS
    • UI Thread FPS
    • RAM 使用
    • 通过 Dev Menu 开启
  • Systrace (Android)

    • 系统级的性能追踪
    • 可以看到每个方法的耗时
    • react-native-systrace 集成
  • Xcode Instruments

    • Time Profiler:CPU 使用分析
    • Core Animation:FPS 和 GPU 分析
    • Allocations:内存分配分析
    • Leaks:内存泄漏检测

4.8.2 生产环境监控

  • 自定义性能打点上报
  • 关键指标:启动时间、页面加载时间、帧率、崩溃率
  • 使用 Sentry / Bugsnag / Firebase Performance 等 APM 工具
  • 自定义原生模块采集 FPS 和内存数据

4.9 React Compiler(React Forget)

  • React 19 引入的自动编译器
  • 自动为组件添加 memoization(自动 memo, useMemo, useCallback)
  • 分析组件代码,识别可以安全缓存的值
  • 减少手动优化的需要
  • 在 RN 新架构中逐步支持

第五部分:八股文中的对比

5.1 React Native vs Flutter

维度React NativeFlutter
语言JavaScript/TypeScriptDart
渲染方式原生组件(通过 Bridge/JSI)自绘引擎(Skia/Impeller)
UI 一致性使用原生组件,平台差异大自绘,跨平台完全一致
性能接近原生(新架构提升大)接近原生(自绘有 GPU 开销)
热重载支持(Fast Refresh)支持(Hot Reload)
OTA 热更新支持(CodePush 等)不支持(AOT 编译)
生态npm 生态,库丰富pub.dev,快速增长
学习曲线Web 开发者友好需学习 Dart
包体积较小较大(包含 Skia 引擎)
动画Reanimated(UI线程)内置动画系统
原生集成方便(大量原生模块)Platform Channel(相对复杂)
社区更成熟、更大快速增长
使用大厂Meta, Microsoft, ShopifyGoogle, BMW, Alibaba
架构基于 React,JSI/FabricWidget 树,Element 树,RenderObject 树

核心区别:

RN 使用原生组件渲染(一个 <Text> 最终是 UILabelTextView),Flutter 使用自绘引擎从像素级别绘制所有UI。这意味着:

  • RN 天然适配平台风格(平台外观变化自动跟随)
  • Flutter UI 完全可控(但需手动适配平台风格)
  • RN 的性能瓶颈在 JS ↔ Native 通信
  • Flutter 的性能瓶颈在 GPU 渲染管线

5.2 React Native vs 原生开发

维度React Native原生(iOS/Android)
开发效率一套代码双端需要分别开发
性能接近原生(90-95%)100%
更新方式热更新 + 应用商店仅应用商店
团队要求全栈/前端开发者需要 iOS/Android 专家
复杂 UI受限(需原生辅助)完全灵活
系统 API通过 Bridge/JSI直接访问
调试相对复杂(多层调试)成熟的调试工具
第三方SDK需要封装直接使用
维护成本较低(一套代码)较高(两套代码)
适用场景业务类 App、快速迭代高性能、系统级应用

5.3 React Native vs Weex vs Uni-app

维度React NativeWeexUni-app
基础框架ReactVue 2Vue 2/3
渲染原生组件原生组件原生 + WebView
维护方MetaApache (原阿里)DCloud
社区活跃度非常高低 (几乎停滞)国内高
跨端范围iOS + AndroidiOS + Android + WebiOS + Android + Web + 小程序
生态极丰富较少丰富(国内)
稳定性中高

5.4 旧架构 vs 新架构对比

维度旧架构(Bridge)新架构(Fabric + JSI)
通信方式JSON 序列化 + 异步 BridgeJSI 直接调用(同步/异步)
序列化开销有(JSON 序列化/反序列化)无(直接内存访问)
渲染器旧 UI ManagerFabric
模块系统NativeModules(全量初始化)TurboModules(懒加载)
类型安全无保证Codegen 生成类型安全代码
同步调用不支持支持
并发渲染不支持支持
Shadow Tree可变不可变(Immutable)
线程模型固定三线程灵活,任意线程可渲染
布局信息读取异步回调同步读取

5.5 Hermes vs JavaScriptCore vs V8

维度HermesJavaScriptCoreV8
编译方式AOT(预编译字节码)JIT(运行时编译)JIT(分层编译)
启动速度最快中等最慢
运行时性能中等中等最快
内存占用最低中等最高
包体积影响字节码更小需包含 JS 源码引擎体积大
GC 策略分代 GC + 增量标记分代 GC分代 GC + 增量标记 + 并发 GC
调试支持Chrome DevTools ProtocolSafari Inspector / ChromeChrome DevTools
RN 默认是(0.70+)否(旧版默认)否(第三方)
Proxy 支持有限完整完整
eval 支持不支持支持支持

5.6 FlatList vs SectionList vs FlashList vs RecyclerListView

维度FlatListSectionListFlashListRecyclerListView
回收机制卸载不可见组件卸载不可见组件真正的视图回收真正的视图回收
性能中等中等优秀优秀
分组支持不支持支持支持(通过类型)支持
API 易用性简单简单简单(兼容 FlatList)较复杂
内存效率一般一般优秀优秀
空白区域常见常见极少极少
维护方React NativeReact NativeShopifyFlipkart

FlashList 为何性能更好:

  1. 真正的视图回收:FlatList 滑出屏幕的组件会被卸载(unmount),新的组件会被挂载(mount)。FlashList 滑出的组件保留实例,只更新 props(类似原生 RecyclerView)
  2. 减少内存分配:不频繁创建/销毁组件,减少 GC 压力
  3. 减少布局计算:复用已有的布局信息
  4. Cell 类型系统:只有相同类型的 Cell 才会互相回收,避免不必要的重渲染

5.7 React Navigation vs react-native-navigation (Wix)

维度React Navigationreact-native-navigation
实现方式纯 JS 实现原生实现
导航栏JS 渲染原生导航栏
页面容器React 组件原生页面容器
性能良好(配合 react-native-screens)更接近原生
定制化高度可定制受限于原生 API
集成难度简单复杂(需改原生代码)
社区活跃度非常高中等
Deep Link内置支持需手动处理
推荐程度官方推荐追求极致性能时使用

5.8 Animated vs LayoutAnimation vs Reanimated

维度AnimatedLayoutAnimationReanimated 2/3
执行线程JS(可开启 native driver)UI 线程UI 线程
API 风格声明式声明式声明式 + 命令式
支持属性transform, opacity(native driver)/ 全部布局属性全部
手势联动有限不支持完美
复杂度中等简单较高
可中断有限不可中断可中断
条件逻辑不支持不支持支持(worklet)
适用场景简单动画布局变化复杂交互动画

5.9 AsyncStorage vs MMKV vs SQLite vs Realm

维度AsyncStorageMMKVSQLiteRealm
API 类型异步同步(JSI)异步/同步同步
数据模型Key-ValueKey-Value关系型对象型
性能极快(30x)中等
数据量少量中等大量大量
查询能力SQL 查询对象查询
加密不支持支持需第三方支持
适用场景简单配置频繁读写的配置结构化数据复杂对象数据

5.10 Redux vs MobX vs Zustand vs Jotai vs Recoil

维度ReduxMobXZustandJotaiRecoil
范式Flux响应式Flux-lite原子化原子化
代码量极少极少
学习曲线
DevTools优秀良好良好良好良好
TypeScript需配置良好优秀优秀良好
Bundle 大小较大较大极小(~1KB)极小中等
精确更新需 selector自动selector自动selector
中间件丰富有限支持有限有限
服务端状态RTK Querymobx-query无内置无内置无内置
适用规模大型中大型中小型中小型中型

5.11 CodePush vs Expo Updates vs 自建热更新

维度CodePushExpo Updates自建方案
维护方MicrosoftExpo自己
差量更新支持支持需自行实现
回滚自动手动需自行实现
灰度发布支持支持(EAS Update)需自行实现
统计内置EAS 仪表板需自行实现
服务可控性低(微软服务)中(Expo 服务)高(完全自控)
成本免费(有限制)免费+付费服务器成本
集成难度低(Expo 项目)

5.12 Class Component vs Function Component

维度Class ComponentFunction Component
语法ES6 Class函数
状态管理this.state + setStateuseState
生命周期生命周期方法useEffect
逻辑复用HOC / Render Props自定义 Hooks
this 绑定需要处理无 this
性能实例创建开销闭包开销
代码量
Error Boundary支持不支持(仍需 Class)
Concurrent Mode部分支持完全支持
未来方向维护推荐

5.13 ScrollView vs FlatList vs VirtualizedList

维度ScrollViewFlatListVirtualizedList
渲染方式全量渲染虚拟化(窗口渲染)虚拟化
内存占用高(全部子组件)低(只渲染可见区域)
适用数据量< 100 项大量大量
嵌套 ScrollView不建议不建议不建议
抽象级别基础高级底层
API 复杂度简单中等较复杂

5.14 useEffect vs useLayoutEffect

维度useEffectuseLayoutEffect
执行时机Commit 后异步执行Commit 的 Layout 阶段同步执行
阻塞渲染不阻塞阻塞(在绘制前执行)
适用场景数据获取、订阅、日志DOM 测量、同步 DOM 操作
性能影响大(阻塞绘制)
RN 中常用需要在渲染前读取/修改布局时

5.15 受控组件 vs 非受控组件

维度受控组件非受控组件
数据来源React stateDOM/原生
更新方式onChange + setStateref 获取值
实时验证容易困难
性能每次输入触发重渲染不触发重渲染
RN TextInputvalue + onChangeTextdefaultValue + ref
表单库react-hook-form, Formikreact-hook-form (默认非受控)

5.16 Stack vs Tab vs Drawer Navigator

维度StackTabDrawer
UI 形式堆叠式页面底部/顶部标签侧边抽屉
页面保持全部保持在栈中全部保持全部保持
内存栈越深越多固定固定
适用场景层级导航主导航辅助导航
手势右滑返回(iOS)左滑打开

5.17 Expo vs Bare React Native

维度Expo (Managed)Bare React Native
上手难度极低中等
原生代码不可直接修改(需 prebuild)完全控制
构建EAS Build(云端)本地 Xcode/AS
原生库受限(需 expo 插件)任意
OTA 更新EAS UpdateCodePush 等
文件系统受限完全访问
推送通知expo-notifications自行集成
适用阶段原型/中小项目大型/复杂项目

第六部分:高难度深底层原理问题

6.1 JSI 的深层工作机制

6.1.1 Host Object 原理

JSI 的核心创新是 Host Object(宿主对象)。在传统 Bridge 模式下,JS 和 Native 通过 JSON 字符串通信。JSI 引入了 Host Object 的概念:

  1. C++ 对象暴露给 JS:C++ 创建一个继承 jsi::HostObject 的对象
  2. 属性拦截:JS 访问该对象的属性时,调用 C++ 的 get() 方法
  3. 方法调用:JS 调用该对象的方法时,直接执行 C++ 代码
  4. 零拷贝:JS 持有的是 C++ 对象的引用,不需要序列化

本质上:JSI 让 JS 直接操作 C++ 对象的指针(通过引用包装),消除了序列化的开销。

6.1.2 JSI 与引擎的解耦

JSI 定义了一组抽象接口(jsi::Runtime),每个 JS 引擎需要实现这些接口:

  • evaluateJavaScript():执行 JS 代码
  • createObject():创建 JS 对象
  • createFunctionFromHostFunction():将 C++ 函数暴露给 JS
  • global():获取全局对象

通过这层抽象,React Native 可以无缝切换 JS 引擎(JSC → Hermes → V8),上层代码无需修改。

6.1.3 JSI 的线程安全问题

  • JSI 对象(jsi::Value, jsi::Object 等)只能在创建它们的 jsi::Runtime 所在的线程访问
  • 跨线程共享需要特殊处理:
    • Shared Values(Reanimated)使用 C++ 层的互斥锁保护
    • TurboModules 使用 CallInvoker 将回调调度到正确的线程

6.2 Fabric 渲染流水线深入

6.2.1 三阶段详解

Render Phase:

  1. React 在 JS 线程执行 render(),生成 React Element Tree
  2. React Element 通过 JSI 调用 C++ 的 ShadowNodeFactory
  3. 创建不可变的 C++ ShadowNode 树(每个节点包含 props 和子节点引用)
  4. ShadowNode 是不可变的:修改意味着创建新节点(Copy-on-Write 语义)

Commit Phase:

  1. 新的 ShadowNode 树被提交(Commit)
  2. 在后台线程执行 Yoga 布局计算(Layout)
  3. 执行 Tree Diffing:对比新旧 ShadowNode 树
  4. 生成 Mutation 指令列表(Create, Delete, Insert, Remove, Update)
  5. 使用引用相等性(指针比较)快速跳过未变化的子树

Mount Phase:

  1. 在主线程执行 Mutation 指令
  2. 创建/更新/删除原生视图
  3. 设置原生视图的属性和布局
  4. 这一阶段的操作必须在主线程执行(iOS UIKit / Android View System 的要求)

6.2.2 Immutable Shadow Tree 的意义

  • 线程安全:不可变对象可以安全地在多线程间共享
  • 高效 Diffing:如果两个节点指针相同,整个子树一定相同,可以跳过
  • 支持并发:多个版本的 Shadow Tree 可以同时存在(Concurrent Mode)
  • 可追溯:保留旧版本用于回滚

6.2.3 C++ 层的统一抽象

Fabric 使用 C++ 实现核心逻辑,跨 iOS/Android 共享:

  • ShadowNode 和 ShadowTree 是 C++ 对象
  • Yoga 布局计算在 C++ 层执行
  • Tree Diffing 和 Mutation 生成在 C++ 层
  • 只有最终的 Mount 操作调用平台特定的 API

6.3 TurboModules 底层机制

6.3.1 模块绑定流程

  1. Codegen 阶段(编译时):

    • 解析 TypeScript/Flow 接口定义
    • 生成 C++ 的 NativeXxxCxxSpec 类(接口定义)
    • 生成 ObjC++ / Java 的胶水代码
  2. 注册阶段(运行时):

    • 模块工厂注册到 TurboModuleManager
    • 但不立即创建模块实例
  3. 访问阶段(首次调用时):

    • JS 代码访问 global.__turboModuleProxy('ModuleName')
    • TurboModuleManager 通过工厂创建模块实例
    • 将实例包装为 JSI Host Object
    • 返回给 JS,后续调用直接通过 JSI

6.3.2 同步调用的实现

  • TurboModules 支持同步方法:JS 调用 → JSI → C++ → Native,在同一个调用栈中完成
  • 适用于需要立即获取结果的场景(如读取本地存储)
  • 注意:同步调用会阻塞 JS 线程,应该只用于极快的操作

6.3.3 与旧 NativeModules 的互操作

  • Bridgeless 模式下提供 Interop Layer
  • 旧的 NativeModules 通过 Interop 被包装为 TurboModules 接口
  • 性能不如原生 TurboModules,但保证兼容性

6.4 Hermes 引擎深入

6.4.1 编译流程

JS 源码 → Hermes 编译器 → Hermes 字节码 (.hbc)
                ↓
          1. Lexer(词法分析)
          2. Parser(语法分析 → AST)
          3. Semantic Validation(语义检查)
          4. IR Generation(中间表示生成)
          5. Optimization Passes(优化)
          6. Bytecode Generation(字节码生成)

6.4.2 字节码格式 (.hbc)

  • 文件头:魔数、版本、哈希
  • 函数表:每个函数的字节码偏移量、参数信息
  • 字符串表:所有字符串的存储(去重)
  • 字节码段:实际的指令序列
  • 正则表达式表:预编译的正则表达式

6.4.3 Hermes GC 详解

GenGC(分代垃圾回收):

  1. Young Generation(新生代)

    • 使用 semi-space copying 算法
    • 分为 From Space 和 To Space
    • 新对象分配在 From Space
    • GC 时将存活对象从 From Space 复制到 To Space
    • 然后交换两个 space
    • 存活多次的对象晋升到 Old Generation
  2. Old Generation(老年代)

    • 使用 mark-compact 算法
    • Mark 阶段:标记所有可达对象
    • Compact 阶段:移动对象消除碎片
    • 支持增量标记:将标记工作分散到多个时间片,避免长暂停

GC 优化策略:

  • Allocation 策略:对小对象使用 bump-pointer allocation(极快)
  • Write Barrier:当老年代对象引用新生代对象时,记录在 remembered set 中,避免全堆扫描
  • 惰性扫描:延迟扫描大对象
  • GC 暂停控制:目标是将 GC 暂停控制在毫秒级

6.4.4 Hermes 为何不支持 JIT

  • 安全限制:iOS 不允许第三方 App 执行动态生成的可执行代码(W^X)
  • 权衡:AOT 编译 + 优化的解释器 在移动端场景下,综合性能(启动 + 运行 + 内存)优于 JIT
  • 确定性:AOT 编译产生确定的性能特征,不像 JIT 有 "warm-up" 期

6.5 Bridge 通信的底层实现

6.5.1 消息格式

Bridge 传输的消息格式:

[
  moduleID,    // Native 模块 ID(数字)
  methodID,    // 方法 ID(数字)
  args         // 参数数组(JSON 序列化)
]

6.5.2 批量处理

  • JS 端不会立即发送每条消息,而是收集在队列中
  • 在以下时机批量刷新队列:
    1. 定时刷新(默认 5ms)
    2. Native 回调 JS 时捎带(piggyback)
    3. 队列满时
    4. 手动调用 flushQueue

6.5.3 Bridge 的瓶颈量化

  • 单条消息的序列化/反序列化:~0.01-0.1ms
  • 大型消息(如长列表数据):可能需要几十毫秒
  • 高频消息(如动画每帧更新):累积延迟导致掉帧
  • 在 16.67ms 的帧预算内,Bridge 通信可能占用 30-50%

6.6 React 的 Lane 优先级模型

6.6.1 Lane 系统

React 18 使用 Lane 位标记系统管理更新优先级:

  • 每个 Lane 是一个 32 位整数的一个 bit
  • 多个 Lane 可以通过位运算组合
  • 优先级从高到低:
    1. SyncLane:同步更新(最高优先级)
    2. InputContinuousLane:连续输入(如拖动)
    3. DefaultLane:默认更新(如 setState)
    4. TransitionLane:过渡更新(startTransition)
    5. IdleLane:空闲更新

6.6.2 在 RN 中的应用

  • 触摸事件处理:SyncLane(立即响应)
  • 数据更新:DefaultLane(正常优先级)
  • 页面切换动画:TransitionLane(可中断)
  • 预加载数据:IdleLane(空闲时执行)

6.6.3 优先级饥饿问题

  • 高优先级更新持续到来时,低优先级更新可能一直得不到执行
  • React 的解决方案:给每个更新设置过期时间(expiration time)
  • 过期的更新会被提升为同步更新,强制执行

6.7 React Native 的事件循环与调度

6.7.1 JS 线程的事件循环

RN 的 JS 线程运行一个类似浏览器的事件循环:

  1. 执行同步代码
  2. 处理微任务(Microtask):Promise.then, queueMicrotask
  3. 处理宏任务(Macrotask):setTimeout, setInterval, I/O 回调
  4. 处理 Bridge/JSI 消息:来自 Native 的调用
  5. 执行 requestAnimationFrame 回调

6.7.2 与浏览器事件循环的区别

维度浏览器React Native
渲染时机RAF → Style → Layout → Paint → CompositeJS 计算 → Bridge/JSI → Native 渲染
requestAnimationFrame与屏幕刷新同步不保证与屏幕刷新同步
requestIdleCallback支持不支持(可 polyfill)
Web Worker支持不原生支持
MessageChannel用于 React 调度器同样用于 React 调度器

6.7.3 React Scheduler

  • React 使用自己的调度器(Scheduler)管理更新
  • 在 RN 中通过 MessageChannelsetTimeout(fn, 0) 实现调度
  • 调度器维护一个优先级任务队列(小顶堆)
  • 每次事件循环取出最高优先级的任务执行
  • 如果任务执行时间超过时间片(默认 5ms),让出控制权

6.8 原生视图的生命周期管理

6.8.1 iOS UIView 在 RN 中的管理

  1. 创建:Fabric 的 Mount 阶段调用 ComponentViewFactory 创建 UIView
  2. 更新 Props:通过 updateProps: 方法更新属性(diff 后只更新变化的属性)
  3. 布局:设置 frame 属性(由 Yoga 计算的坐标和尺寸)
  4. 挂载addSubview: 添加到父视图
  5. 卸载removeFromSuperview 从父视图移除
  6. 销毁:当没有任何引用时由 ARC 自动回收

6.8.2 Android View 在 RN 中的管理

  1. 创建ViewManager.createViewInstance() 创建 View
  2. 更新 Props@ReactProp 注解的方法被调用
  3. 布局:调用 measure() + layout() 设置位置和大小
  4. 挂载addView() 添加到 ViewGroup
  5. 卸载removeView() 从 ViewGroup 移除
  6. 销毁onDropViewInstance() 清理资源

6.8.3 View Flattening

  • React 中大量使用嵌套 <View> 组件,但很多只用于布局,没有视觉效果
  • View Flattening 优化:将这些 "纯布局" View 从原生视图层级中移除
  • 只保留有视觉属性(背景色、边框、阴影等)的 View
  • 减少原生视图数量 → 减少内存 → 加快渲染
  • Fabric 自动执行 View Flattening

6.9 跨线程数据共享的挑战

6.9.1 问题本质

RN 中有多个线程(JS, UI, Shadow, Native Module),跨线程数据共享面临:

  1. 数据竞争:多线程同时读写同一数据
  2. 一致性:确保各线程看到的数据状态一致
  3. 性能:锁竞争会降低性能

6.9.2 RN 中的解决方案

旧架构:

  • 通过 Bridge 消息传递,本质上是 "消息传递" 模式
  • 各线程维护自己的数据副本
  • 不存在共享可变状态

新架构:

  • JSI 引入了共享引用的概念
  • 使用 C++ 的 std::mutexstd::shared_mutex 保护共享数据
  • Fabric 的 Shadow Tree 使用不可变数据结构,避免并发修改
  • CallInvoker 确保回调在正确的线程执行

Reanimated 的方案:

  • Shared Values 使用 C++ 层的原子操作或锁保护
  • worklet 运行在独立的 JS 运行时,与主 JS 运行时隔离
  • 通过共享内存(而非消息传递)交换数据

6.10 Metro Bundler 的模块解析算法

6.10.1 解析流程

  1. 从入口文件(index.js)开始
  2. 遇到 import/require 时:
    • 相对路径./module):按路径查找
    • 包名react-native):依次查找 node_modules
  3. 文件扩展名解析顺序:.ios.js.native.js.js(iOS 平台)
  4. 目录解析:查找 index.jspackage.jsonmain 字段

6.10.2 平台特定模块

Metro 支持平台特定的模块解析:

Component.js        // 通用
Component.ios.js    // iOS 专用
Component.android.js // Android 专用
Component.native.js  // iOS + Android 共用
Component.web.js     // Web 专用

解析优先级:平台后缀 > .native > 通用

6.10.3 Metro 与 Webpack 的区别

维度MetroWebpack
目标平台React NativeWeb(也可其他)
代码分割有限支持完善支持
Tree Shaking有限完善
HMRFast RefreshHot Module Replacement
增量构建内存缓存(极快)文件缓存
配置简单复杂但灵活
插件系统简单强大

6.11 React Native 的触摸事件处理底层

6.11.1 iOS 触摸事件链

  1. 系统通过 UIResponder 链传递 UITouch 事件
  2. RN 的根视图 RCTRootContentView 接收触摸事件
  3. 通过 hit-testing 找到目标视图
  4. 事件被包装为 RCTTouchEvent
  5. 旧架构:序列化后通过 Bridge 发送到 JS
  6. 新架构:通过 JSI 直接传递到 JS

6.11.2 Android 触摸事件链

  1. Activity 通过 dispatchTouchEvent 传递 MotionEvent
  2. RN 的 ReactRootView 接收事件
  3. 通过 JSTouchDispatcher 收集触摸点信息
  4. 事件被批量发送到 JS

6.11.3 手势冲突处理

  • 原生手势(ScrollView 滑动)和 JS 手势可能冲突
  • disallowInterceptTouchEvent(Android):阻止父视图拦截触摸事件
  • Gesture Handler 使用独立的手势识别系统,与原生手势并行
  • 通过手势竞争机制(simultaneousHandlers, waitFor)解决冲突

6.12 React Native 的 Accessibility 实现

6.12.1 iOS VoiceOver

  • accessible={true}:将组件标记为无障碍元素
  • accessibilityLabel:映射到 UIView 的 accessibilityLabel
  • accessibilityRole:映射到 UIAccessibilityTraits
  • accessibilityActions:自定义无障碍操作

6.12.2 Android TalkBack

  • accessible={true}:设置 importantForAccessibility
  • accessibilityLabel:映射到 contentDescription
  • accessibilityRole:映射到 className(TalkBack 根据 className 确定控件类型)

6.12.3 无障碍焦点管理

  • accessibilityElementsHidden(iOS)/ importantForAccessibility(Android)
  • accessibilityViewIsModal:将无障碍焦点限制在当前视图(模态窗口)
  • 自定义焦点顺序需要原生代码辅助

6.13 深入理解 Yoga 的 Flexbox 实现

6.13.1 Yoga 与 CSS Flexbox 的差异

特性CSS FlexboxYoga (RN)
flexDirection 默认值rowcolumn
flex 简写flex-grow flex-shrink flex-basis等价于 flexGrow(简化)
position: absolute相对于最近的 positioned 祖先相对于父元素
overflow 默认值visiblehidden(Android)/ visible(iOS)
百分比 padding参照父元素对应方向参照父元素宽度

6.13.2 Yoga 的布局算法

  1. 确定主轴和交叉轴(根据 flexDirection)
  2. 收集弹性子项(flex > 0 的子项)
  3. 第一轮布局
    • 固定尺寸的子项直接确定大小
    • 弹性子项根据 flexBasis 确定初始大小
  4. 分配剩余空间
    • 如果有剩余空间:按 flexGrow 比例分配
    • 如果空间不足:按 flexShrink 比例收缩
  5. 确定交叉轴对齐(alignItems, alignSelf)
  6. 多行处理(flexWrap)
  7. 处理绝对定位子项

6.13.3 自定义测量函数

  • 某些组件需要原生测量(如 Text 组件需要知道文本的渲染尺寸)
  • Yoga 支持为节点设置自定义测量函数
  • 测量函数在布局计算时被调用
  • 返回节点的固有尺寸(intrinsic size)
  • Text 组件的测量函数会调用原生文本排版引擎(iOS: TextKit, Android: StaticLayout)

6.14 跨平台字体渲染差异

6.14.1 文本渲染管线

iOS:

  • TextKit 框架 → Core Text → Core Graphics → GPU
  • 使用子像素抗锯齿(Subpixel Anti-aliasing)
  • 支持动态字体大小(Dynamic Type)

Android:

  • StaticLayout / DynamicLayout → Skia → OpenGL / Vulkan
  • 使用灰度抗锯齿(Grayscale Anti-aliasing)
  • 不同厂商可能定制字体渲染

6.14.2 常见问题

  • 相同字体大小在不同平台显示不同
  • 行高计算方式不同(iOS 和 Android 对 lineHeight 的处理不同)
  • 字体加粗的 weight 映射不一致
  • 自定义字体在 Android 上可能有 baseline 偏移

6.14.3 解决方案

  • includeFontPadding: false(Android):移除额外的字体 padding
  • 精确指定 lineHeight(避免使用默认值)
  • 统一使用自定义字体,减少平台差异
  • 使用 textAlignVertical(Android)调整垂直对齐

6.15 新架构下的并发渲染

6.15.1 同步渲染 vs 并发渲染

同步渲染(传统):

  • 一旦开始渲染,必须完成才能响应用户交互
  • 大型组件树的渲染会阻塞用户交互

并发渲染(Concurrent):

  • 渲染可以被中断和恢复
  • 高优先级更新可以打断低优先级渲染
  • 多个版本的 UI 可以同时 "准备"

6.15.2 Fabric 如何支持并发

  1. 不可变 Shadow Tree:每次渲染产生独立的 Shadow Tree 版本
  2. 多 Tree Revision:可以同时存在多个版本的 Shadow Tree
  3. 原子提交:Shadow Tree 的提交是原子操作(成功或失败,没有中间状态)
  4. 优先级中断:高优先级更新到来时,低优先级的渲染可以被丢弃

6.15.3 实际应用场景

  • 搜索输入框 + 搜索结果列表:输入是高优先级,结果渲染是低优先级
  • 页面切换:切换动画是高优先级,新页面内容渲染可以延迟
  • 大列表滚动 + 数据更新:滚动是高优先级,数据更新可以在空闲时处理

6.16 React Native 的错误边界与崩溃恢复

6.16.1 JS 层面的错误处理

  • Error Boundary:捕获子组件树中的 JS 错误,渲染 fallback UI
  • global.ErrorUtils:RN 提供的全局错误处理(区别于 Web 的 window.onerror)
  • Promise rejection:未捕获的 Promise rejection 可以通过 global.onunhandledrejection 处理

6.16.2 原生层面的崩溃处理

  • iOS:NSSetUncaughtExceptionHandler + signal handlers
  • Android:Thread.setDefaultUncaughtExceptionHandler
  • 崩溃报告工具:Sentry, Crashlytics, Bugsnag

6.16.3 JS 崩溃不会导致原生应用崩溃

  • JS 错误会导致红屏(开发)或白屏(生产),但原生进程仍在运行
  • 可以通过 Error Boundary 优雅降级
  • 可以通过重新加载 JS Bundle 恢复(DevSettings.reload()
  • 但原生层面的 crash 会直接终止进程

6.17 内存映射(mmap)在 RN 中的应用

6.17.1 mmap 原理

  • 将文件内容直接映射到进程的虚拟地址空间
  • 不需要将整个文件读入内存(按需加载,Page Fault 时才从磁盘读取)
  • 多进程可以共享同一映射(减少内存占用)
  • 操作系统管理页面的换入/换出

6.17.2 Hermes 中的 mmap

  • Hermes 字节码文件 (.hbc) 通过 mmap 加载
  • 启动时不需要将整个字节码读入内存
  • 只有执行到的代码页才会被加载
  • 操作系统可以在内存压力时回收已加载但暂时不用的页面

6.17.3 MMKV 中的 mmap

  • 数据文件通过 mmap 映射到内存
  • 读写操作直接操作内存映射区域
  • 由操作系统负责将脏页写回磁盘
  • 进程崩溃时,操作系统仍会将脏页刷盘(大部分情况下数据不丢失)
  • 比文件 I/O 快一个数量级

6.18 JavaScript → 原生渲染的完整链路

以一个简单的 <Text>Hello</Text> 为例,追踪其从 JS 到屏幕显示的完整过程:

新架构(Fabric + JSI):

  1. JS 线程

    • React 执行 render(),创建 <Text> 的 React Element
    • createElement 通过 JSI 调用 C++ 的 ShadowNodeFactory.createNode()
  2. C++ 层

    • 创建 TextShadowNode(不可变的 C++ 对象)
    • 存储 props(text content, style 等)
    • TextShadowNode 包含自定义测量函数
  3. Commit Phase(后台线程)

    • 提交新的 Shadow Tree
    • Yoga 执行布局计算
    • 调用 Text 的自定义测量函数 → 调用原生文本排版引擎测量文本尺寸
    • Tree Diffing 生成 Mutation 指令
  4. Mount Phase(主线程)

    • iOS:创建 RCTParagraphComponentView(UIView 子类),设置 NSAttributedString,调用 setNeedsDisplay
    • Android:创建 ReactTextView(TextView 子类),设置 SpannableString,调用 requestLayout
  5. 原生渲染管线

    • iOS:CoreAnimation → Metal/OpenGL → GPU → 屏幕
    • Android:Canvas → Skia → OpenGL/Vulkan → GPU → 屏幕

整个过程从 JS 到屏幕,在新架构下可以在 1-2 帧(16-33ms)内完成。

6.19 React Native 的安全考量

6.19.1 JS Bundle 安全

  • JS Bundle 是明文的(或 Hermes 字节码可被反编译)
  • 敏感逻辑不应放在 JS 层
  • 代码混淆:metro.config.js 配置混淆
  • Hermes 字节码比 JS 源码更难逆向,但仍可反编译

6.19.2 网络通信安全

  • SSL Pinning:验证服务器证书(react-native-ssl-pinning)
  • 请求签名:防止篡改
  • Token 安全存储:使用 Keychain (iOS) / Keystore (Android) 而非 AsyncStorage

6.19.3 数据存储安全

  • Keychain (iOS) / Keystore (Android):加密存储敏感数据
  • MMKV 加密模式:AES-128 加密
  • SQLCipher:SQLite 加密扩展
  • 避免在日志中输出敏感信息

6.20 Worklet 运行时的隔离与通信

6.20.1 双运行时架构(Reanimated)

Reanimated 在 UI 线程维护一个独立的 JS 运行时:

  1. 主 JS 运行时(JS 线程):运行 React 代码和业务逻辑
  2. Worklet 运行时(UI 线程):运行 worklet 函数

两个运行时通过 Shared Values(C++ 层)共享数据。

6.20.2 Worklet 的序列化与传输

  1. Babel 插件在编译时识别 'worklet' 标记
  2. 提取函数体的 AST,分析闭包捕获的变量
  3. 将函数体序列化为字符串
  4. 将捕获的变量打包为初始化数据
  5. 运行时:在 UI 线程的 JS 运行时中 eval 函数字符串,绑定捕获变量

6.20.3 跨运行时调用的开销

  • runOnJS(fn)(args):从 UI 线程调度到 JS 线程
    • 参数需要序列化(简单类型直接传递,复杂类型 JSON 序列化)
    • 有 1-2 帧的延迟
  • runOnUI(worklet)(args):从 JS 线程调度到 UI 线程
    • 类似的序列化和调度开销
  • 应该尽量减少跨运行时调用

补充:常见高频面试题精选

Q1: React Native 是如何实现跨平台的?

React Native 通过抽象层实现跨平台。JS 层定义通用的组件和 API(如 <View>, <Text>),这些组件在不同平台上映射到不同的原生视图(iOS 的 UIView / Android 的 android.view.View)。通信层(Bridge/JSI)负责 JS 与 Native 的数据交换。布局引擎 Yoga(C++)跨平台共享布局计算逻辑。新架构中,Fabric 的 C++ 核心也是跨平台共享的。

Q2: 为什么 React Native 的性能比 WebView 好?

  • WebView 的 DOM 操作需要经过浏览器渲染管线(Parse HTML → CSSOM → Layout → Paint → Composite),且在一个独立进程中
  • RN 直接操作原生视图,不经过 DOM/CSSOM
  • RN 的布局计算(Yoga)直接产出像素坐标,不需要 CSS 级联计算
  • RN 可以利用原生组件的硬件加速
  • 但 RN 的 JS ↔ Native 通信开销是其性能瓶颈(新架构大幅改善)

Q3: 什么情况下 React Native 不适用?

  • 高度依赖 GPU 计算的应用(游戏、AR/VR)
  • 需要大量底层系统 API 的应用(系统工具类)
  • 对性能有极致要求的场景(如实时视频编辑)
  • UI 高度依赖平台原生特性的应用
  • 需要复杂自定义绘制的应用(如绘图工具)

Q4: React Native 新架构的核心价值是什么?

  1. 消除序列化开销:JSI 让 JS 直接操作 C++ 对象
  2. 支持同步操作:可以同步读取布局信息、同步调用 Native 方法
  3. 类型安全:Codegen 保证 JS 和 Native 接口一致
  4. 懒加载:TurboModules 按需初始化,加快启动
  5. 并发渲染:Fabric 支持 React 18 的 Concurrent Features
  6. 统一 C++ 核心:核心逻辑跨平台共享,减少平台差异

Q5: 如何监测和优化 React Native 应用的帧率?

  1. 监测

    • 开发时:RN Performance Monitor(Dev Menu 中开启)
    • iOS:Instruments → Core Animation FPS
    • Android:adb shell dumpsys gfxinfo
    • Flipper 性能插件
  2. 常见掉帧原因及优化

    • JS 线程阻塞 → 将耗时操作移到原生或使用 InteractionManager
    • 过多重渲染 → React.memo, useMemo, useCallback
    • 复杂列表 → 使用 FlashList, 优化 FlatList 参数
    • 动画在 JS 线程 → 使用 ReanimateduseNativeDriver
    • 大量原生视图 → 减少视图嵌套层级(View Flattening)

Q6: React Native 的 Fast Refresh 与 Hot Module Replacement 有什么区别?

  • Fast Refresh 是 React 专属的实现,知道 React 组件的边界

    • 编辑函数组件时,只重新执行该组件,保留 state
    • 编辑类组件时,重新挂载(不保留 state)
    • 编辑非组件文件时,重新加载引用该文件的所有组件
    • 语法错误不会导致全量刷新(修复后自动恢复)
  • HMR 是通用的模块热替换机制

    • 不理解 React 组件的边界
    • 总是需要手动定义 module.hot.accept
    • Fast Refresh 基于 HMR 实现,但添加了 React 特有的逻辑

本文档覆盖了 React Native 从底层引擎到上层应用、从旧架构到新架构、从基础概念到高级原理的全面内容。建议结合实际项目经验理解各知识点,做到融会贯通。