React Native
React Native 面试的绝对核心是新老架构的差异(Bridge vs JSI)、渲染原理(Yoga 引擎)以及性能优化(FlatList、动画、Hermes 引擎)。
常见面试题
Q1:RN 开发和 React 网页开发的核心区别是什么?重点要关注什么?
面试官意图:考察你是否有真实的 RN 开发经验,是否理解从 Web 到 Native 的思维转换。
核心区别:
- 渲染引擎不同:网页最终渲染为 DOM,而 RN 的组件(如
<View>,<Text>)最终会被底层的 Yoga 引擎映射为真实的 Native UI 控件(iOS 的UIView,Android 的ViewGroup)。 - 样式隔离与局限:
- RN 没有 CSS 级联和全局样式,样式是内联且强隔离的。
- 不支持
hover、不支持复杂的 CSS 动画(需使用AnimatedAPI 或 Reanimated 库)。
- 事件系统差异:RN 没有 DOM 事件冒泡,取而代之的是
PanResponder或第三方的react-native-gesture-handler来处理复杂的屏幕滑动和手势。
开发时必须重点关注的 3 件事:
- 平台差异处理:iOS 和 Android 在状态栏高度、刘海屏适配(SafeArea)、键盘遮挡甚至基础组件(如时间选择器)上的表现差异极大,必须频繁使用
Platform.OS进行分支处理。 - 性能与掉帧:网页里随意的一个
setState可能只会引起微小的重绘,但在 RN 的老架构里,频繁跨 Bridge 传递数据会导致极其严重的 UI 掉帧(尤其是长列表和复杂动画)。 - 脱离浏览器的环境限制:没有
window、document、localStorage(用AsyncStorage替代),没有 Cookies(网络请求需手动在 Header 塞 Token)。
Q2:RN 中的 View 和 Web 中的 div 有什么区别?
- 渲染本质:Web 中的
div最终会变成浏览器 DOM 树中的一个节点;而 RN 中的<View>在运行时会被 Yoga 引擎计算布局,最终映射并渲染为真实的原生控件(在 iOS 上是UIView,在 Android 上是ViewGroup)。 - 样式局限:
<View>不支持 CSS 所有的属性(例如没有伪类:hover,没有子选择器,所有样式只能内联或使用StyleSheet.create)。
Q2:RN 的 Flexbox 布局和 Web 的 Flexbox 有什么不同?
虽然 RN 使用了 Flexbox 布局模型(由 Yoga 引擎实现),但与 Web 还是有一些核心差异:
- 主轴方向不同:Web 的
flex-direction默认是row(水平),而 RN 的默认值是column(垂直),因为手机屏幕通常是竖屏的。 - 默认对齐方式不同:RN 的
alignItems默认是stretch。 - 属性支持不全:RN 并不支持 Web Flexbox 的所有属性(比如不支持
flex-wrap: wrap-reverse等高级属性)。
Q3:RN 中图片加载 <Image> 为什么有时会显示不出来?
- 网络图片必须指定宽高:在 Web 中,给
<img>标签一个src,浏览器加载完图片后会自动撑开宽高;但在 RN 中,加载网络图片(uri方式)必须明确指定width和height,否则默认宽高为 0,图片无法显示。 - 本地图片:使用
require('./img.png')引入的本地图片,RN 可以在打包时获取其尺寸,因此不需要强制设置宽高。
1. 核心原理与渲染流程
React Native (RN) 的本质是:用 JavaScript 写逻辑,用原生 (iOS/Android) 渲染 UI。
传统渲染流程:
- JS 层:React 代码运行在 JS 线程中,生成 Virtual DOM。
- Bridge (桥):JS 线程通过 Bridge 将 UI 更新指令序列化成 JSON 字符串,异步发送给 Native 层。
- Shadow 线程 (Yoga):接收到指令后,通过 Yoga 引擎(C++ 编写的 Flexbox 布局引擎)计算各个元素的实际物理大小和位置。
- UI 线程 (Main 线程):拿到布局信息后,调用原生控件(如
UIView或ViewGroup)进行最终的渲染。
2. 架构演进:老架构 vs 新架构
在高级面试中,RN 的新老架构对比是出现频率最高的问题。
2.1 老架构 (Legacy Architecture)
老架构的三大线程(JS 线程、UI 线程、Shadow 线程)之间互相隔离,所有的通信都必须经过 Bridge。
- 致命瓶颈:Bridge 通信是异步的、批处理的且需要 JSON 序列化/反序列化的。如果频繁进行 JS 和 Native 之间的通信(如复杂的动画、滑动事件监听),Bridge 就会发生拥堵,导致页面掉帧、白屏。
2.2 新架构 (New Architecture)
为了解决 Bridge 的性能瓶颈,RN 团队推出了新架构,核心包含以下四大组件:
- JSI (JavaScript Interface):新架构的灵魂!彻底废弃了 Bridge。
面试官高频追问:“为什么 JSI 相比 Bridge 能带来质的性能飞跃?” (核心亮点)
- 干掉序列化开销 (内存共享):老架构下,JS 和 Native 每次通信都必须经历
JSON.stringify和JSON.parse的漫长过程,遇到大数据结构时极其消耗 CPU 且占用内存。JSI 允许 JS 引擎直接持有 C++ 对象(HostObject)的指针引用,双方通过内存地址直接读写数据,真正实现了零拷贝 (Zero-Copy)。 - 支持同步调用 (打破异步壁垒):老架构的 Bridge 强制所有通信异步进入队列执行,导致 JS 无法实时干预 Native 的高频 UI 事件(如复杂手势、列表滚动),这是以前掉帧和白屏的元凶。JSI 允许 JS 同步调用 Native 函数,就像调用普通 JS 函数一样极速响应。
- 引擎彻底解耦:JSI 作为一个抽象的 C++ 接口层,解除了 RN 与特定 JS 引擎的强绑定。这使得 RN 能够轻松替换掉老旧的 JSC (JavaScriptCore) 引擎,顺理成章地换上了 Meta 专为 RN 打造的高效 Hermes 引擎。
- 干掉序列化开销 (内存共享):老架构下,JS 和 Native 每次通信都必须经历
- Fabric (新渲染系统):基于 JSI,UI 的渲染更新变成了同步操作,这使得 RN 能够完美支持 React 18 的并发模式 (Concurrent Mode) 和 Suspense,彻底解决了快速滑动时的白屏问题。
- TurboModules:新一代原生模块系统。得益于 JSI,原生模块可以实现懒加载 (Lazy Load),只有在 JS 实际调用时才去初始化,大幅缩短了 App 的启动时间 (TTI)。
- Codegen:代码生成工具。通过静态类型(TypeScript/Flow)自动生成 C++ 接口代码,保证 JS 和 Native 通信时的类型安全。
3. 高频性能优化场景
3.1 长列表优化 (FlatList 优化)
面试官常问:“FlatList 滑动卡顿怎么优化?”
getItemLayout:如果列表项高度固定,提供这个方法可以跳过动态计算高度的步骤,极大提升渲染速度。initialNumToRender:控制首屏渲染的条数,不要设置太大,保证首屏秒开。windowSize:控制渲染窗口的大小(默认是 21,即前后各 10 屏)。适当调小可以减少内存占用。removeClippedSubviews:对于大列表,开启后可以将屏幕外的视图从视图层级中移除,节省内存。- 不要在
renderItem中使用匿名函数或内联对象,避免子组件不必要的重渲染。
3.2 动画优化
在老架构中,如果用 JS 线程的 requestAnimationFrame 去驱动动画,每一帧都要穿过 Bridge,必卡无疑。
- 方案 1:开启原生驱动。使用 Animated 库时,务必加上
useNativeDriver: true。这样动画配置会一次性发给 Native 线程,由 Native 线程在 UI 线程直接执行,完全不占用 JS 线程。 - 方案 2:使用 Reanimated 库。这是目前 RN 动画的终极解决方案,通过 Worklet 机制将动画逻辑直接运行在 UI 线程。
3.3 引擎与打包优化
- Hermes 引擎:Meta 专为 RN 打造的 JS 引擎。核心优势是 AOT (Ahead-of-Time) 编译。它在打包阶段就将 JS 编译成了字节码 (Bytecode),使得 App 启动时不需要再解析 JS,大幅提升启动速度并降低内存占用。
- 拆包 (Bundle Split):随着业务变大,将基础库 (React, RN) 和业务代码拆分,可以实现更高效的热更新。
4. 跨端技术横向对比 (RN vs Flutter vs WebView)
这也是一道经典的架构选型题:
| 特性 | WebView (H5/Cordova) | React Native (RN) | Flutter |
|---|---|---|---|
| 渲染引擎 | 浏览器内核 (Webkit/Blink) | 原生控件 (OEM Widgets) | 自研渲染引擎 (Skia/Impeller) |
| 语言 | JS/HTML/CSS | JavaScript / TypeScript | Dart |
| 性能 | 最差 (受限于 DOM 和 JS 单线程) | 较好 (Native 渲染,但有跨语言通信损耗) | 最好 (直接编译为机器码,自绘 UI) |
| UI 一致性 | 较好 | 差 (依赖两端原生控件,需分别适配) | 极好 (像素级自绘,双端完全一致) |
| 动态化能力 | 极强 (随时发版) | 强 (支持 CodePush 热更新) | 极弱 (iOS 严格限制执行动态机器码) |
| 生态与基建 | 极度繁荣 | 非常繁荣 (共享 npm 生态) | 一般 (Dart 生态相对独立) |
总结话术:
- 如果对动态化(热更新)有强需求,且团队主要是前端开发,React Native 是首选。
- 如果对性能和多端 UI 一致性要求极高,且能接受重新学习一门语言,Flutter 更胜一筹。
- 如果只是为了在 App 里内嵌一些轻量级、经常变动的活动页,使用 WebView 即可。