Skip to content
React Native 概述
React Native 是一个用 JavaScript 和 React 编写原生移动应用的框架。它不把网页套进 App,而是直接使用平台的内置 UI 组件——你在代码里写的 <View>、<Text>,最终会变成 iOS 的 UIView、Android 的 TextView。
这套机制解决了一个很实际的问题:团队已经会写 React,不想为 iOS 和 Android 各维护一套代码,但又要保留原生级别的交互体验。
背景
移动开发长期存在两条路:用各自平台的语言(Swift/Kotlin)写纯原生应用,或者用 Web 技术包一个壳(WebView)。前者性能好、体验好,但人力翻倍;后者可以复用前端代码,但做不到原生绘制,交互总觉得差一点。
React Native 挤进中间地带。它保留 React 的组件模型和声明式 UI,但在渲染层完全不走 Web 引擎,而是由平台原生控件接管。你依然用 JavaScript 写页面逻辑,界面却是由真实的原生控件绘制出来的。
这一点和 Java 的口号“写一次,到处运行”有本质区别。React Native 的目标是“Learn Once, Write Anywhere”——你学一套 API,大多可以跨端,但如果某块体验必须平台特化,你得分别写代码。它不承诺完全消除平台差异,只是压缩两端的重复部分[11]。
原生、WebView 与 React Native 的对比
在 React Native 出现之前,Hybrid 方案主要靠 WebView 嵌入 H5 页面。这种模式可以快速上线、动态发布,但滚动、动画、手势等交互很难做到原生级的流畅度。
纯原生开发没有这个问题,界面完全由平台控件拼出来,滑动帧率、触摸响应都无中间层损耗。代价是两套代码、两套发布节奏。
React Native 的选择是保留原生渲染这一头,砍掉 WebView 的 DOM 和 CSS 布局引擎这一头,中间用 JavaScript 跑业务逻辑。换句话说:
- 渲染由平台自带控件完成,不走 Web 渲染管线。
- 布局用 Flexbox 计算,但不依赖浏览器 CSS 引擎,而是通过 Yoga 布局引擎在原生层算出坐标。
- 交互逻辑(点击、手势、状态变更)在 JavaScript 侧处理,然后通过桥梁驱动原生组件更新。
这带来了一个关键权衡:开发效率更接近 Web,但你需要一个稳定的“JavaScript ↔ 原生”通信通道。这个通道,就是 Bridge。
Bridge 的通信模型
Bridge 是 React Native 旧架构的核心。它所做的事情是把 JavaScript 的调用翻译成原生能懂的指令,再把结果传回去。
整个通信可以分成三个角色:
- JavaScript 线程——运行你的业务代码、React 的 diff 计算。
- 原生主线程(UI 线程)——负责界面绘制、触摸事件分发。
- Shadow 线程——专门跑 Yoga 布局计算,产出布局信息给 UI 线程。
Bridge 承载着异步消息。比如你这样写:
javascript
import { NativeModules } from 'react-native';
NativeModules.CameraModule.takePhoto();这条调用并不会立刻执行拍照,而是被序列化成一条 JSON 消息,丢进 Bridge 队列。原生侧在合适的时候从队列取出消息,交给对应的 CameraModule 原生实现去干活。拍照完成后,原生侧再把结果包装成消息推回 JavaScript 队列,触发你注册的回调。
之所以强调“异步”,是因为 Bridge 内部在同一时刻通常只处理一条消息。如果 JavaScript 线程频繁地、大块地往 Bridge 扔东西,就可能造成原生侧 UI 刷新被拖慢。这个模型类似事件循环:消息按顺序入队、出队、派发。
Bridge 发送的不仅是 API 调用,也包括你的视图变更指令。当你在 React 组件里 setState,React diff 出变化后,这些变化也是通过 Bridge 传给原生,再由原生 UI 线程更新对应的控件。因此,Bridge 拥塞会直接影响渲染速度。
为了避开 Bridge 的瓶颈,早期有一个应急手段叫 setNativeProps——它绕过 React 的 state → diff → Bridge 这条链路,直接从 JavaScript 侧变更原生组件属性。对于需要频繁更新又不想触发完整渲染流程的组件(比如动画中的拖拽),这能缓解不少压力,但代价是代码的可维护性和可预测性下降[5]。
从 JSX 到原生视图
就算只写一个最简单的界面,也能看到整个链路。下面这段代码跑在手机上,你会在屏幕中央看到“Hello React Native”:
jsx
import React from 'react';
import { View, Text } from 'react-native';
export default function App() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Hello React Native</Text>
</View>
);
}在 JavaScript 层,React 把这棵组件树转换成虚拟 DOM 树,再 diff 出变化,通过 Bridge 把“创建一个居中的 View,里面放一个 Text”这样的指令传给原生侧。原生侧收到后,用 UIView/TextView 渲染出来。
这里 <View> 和 <Text> 这些内置组件不是 HTML 元素,而是对平台控件的直接包裹。React Native 没有浏览器 DOM,也没有 CSS 层叠——你写的 style 属性最终被 Yoga 转换为布局参数,再由原生布局系统消化。所以,flex: 1 这个写法在 Android 和 iOS 上都能工作,但具体的渲染细节会受平台特性影响。
新架构:JSI、Fabric 与 Turbo Modules
Bridge 的异步批处理模式对多数页面是够用的,但在一些场景下会暴露出问题:首屏初始化慢(需要提前加载大量原生模块)、高频交互(如手势跟随)延迟明显、JavaScript 与原生之间的类型转换开销过高。
新架构用三个概念重新设计了这一层:
- JSI (JavaScript Interface) 取代 Bridge,让 JavaScript 可以直接持有原生对象的引用,并通过 C++ 层同步调用原生方法,不再经过 JSON 序列化。
- Fabric 是新版渲染系统,不再将布局计算单独跑在 Shadow 线程,而是让 UI 线程与 JavaScript 线程可以更直接地协作更新组件树。
- Turbo Modules 改变原生模块的加载方式。旧架构会在启动时加载所有原生模块,Turbo Modules 只在实际调用到时才加载对应模块,减少启动时间。
JSI 的出现,让“同步调用原生函数”成为可能。以前 NativeModules.Camera.takePhoto() 必须异步,现在可以在某些条件下直接拿到同步返回值。这意味着一些对延迟极度敏感的操作(如动画中的手势跟踪)不再需要绕 Bridge 的队列,可以直接在主线程上完成计算。
这套架构在当前版本中已经可用,但迁移过程中仍然需要评估第三方库对新架构的兼容性。旧架构不会立刻消失,新老架构将在相当长一段时间内共存。
跨平台的含义
“Learn Once, Write Anywhere” 并不是说同一套代码能在任何地方无差别运行。React Native 提供的跨平台 GUI 抽象没有覆盖所有平台 API。比如,如果你想用平台的蓝牙、HealthKit、媒体选择器,通常需要引入或自己写原生模块。
React Native 可以帮助你共享绝大部分业务逻辑、网络层、状态管理、样式布局等,但 UI 细节、平台独有的交互模式往往需要按平台分开处理。代码里偶尔会看到这样的分支:
javascript
import { Platform, Text } from 'react-native';
const instruction = Platform.select({
ios: '按右上角按钮',
android: '点击返回键',
});
<Text>{instruction}</Text>这种 Platform.select 是框架主动暴露的一个分岔口。它承认跨平台抽象终有边界,与其硬搞一套完全不区分平台的写法,不如在你需要的时候用最直白的方式处理差异。
边界还不止平台差异。React Native 渲染的是原生控件,但布局计算依赖 Yoga,部分 CSS 特性(如 display: inline 或复杂的网格布局)没有实现;动画上,虽然能走原生动画驱动,但复杂的交互动画可能仍然要落到平台原生代码上。
适用场景
结合上述特点,可以勾画出一个适用决策清单。
适合 React Native 的场景:
- 团队已有 React 技术栈,且业务界面以表单、列表、信息展示为主。
- 项目不需要重度使用平台专属硬件能力(或者可以接受封装少量原生模块)。
- 要求快速迭代,同时希望界面交互接近原生应用。
- 已经在 WebView 方案中运行的部分页面,可以逐步用 React Native 重写性能敏感的模块[10]。
需要三思的场景:
- 核心功能重度依赖复杂动画、游戏级渲染或高频率传感器数据。
- 界面风格与平台标准组件差异极大,需要大量重写原生控件。
- 团队缺少原生开发经验,但需求中又必须频繁定制动平台特定功能。
- 应用体量极小,纯原生开发本身就不成负担,引入 React Native 反而增加依赖。
生态与工具链
React Native 的工具链围绕 Metro 打包器展开。Metro 负责编译 JSX/TypeScript、打包 bundle,并支持热重载。开发阶段用 react-native 命令行启动模拟器,代码变更后自动推送更新。
不少项目会引入 Expo。Expo 在 React Native 基础上加了一层工具和服务,省去 Xcode/Android Studio 配置阶段,适合快速原型验证。但它对原生模块的管理有一定限制,需要评估是否能满足后续需求。
常用配合包括:
- 导航:
@react-navigation/native是事实上的导航方案,不过本篇不展开。 - 状态管理:简单的
useState+useReducer够用,复杂项目常接 Redux 或 Zustand。 - 调试:React DevTools 可查看组件树,Chrome DevTools 可用于断点调试和变量审查。需要注意,启用 live reload 后断点会失效,必须重新 reload 应用;调试进行中,DevTools 所在标签页必须保持在前台,否则应用会变得明显卡顿[3]。
注意点
- 大型列表必须使用
FlatList,它会进行虚拟化处理,只渲染视口附近的子组件;ScrollView会一次性渲染全部子组件,数据量大时性能会急剧下降[1]。 - 使用
React.memo包裹列表项等子组件,阻止因父组件重渲染导致的不必要更新。默认通过浅比较 props 决定是否跳过渲染[2]。 - 手势处理借助
PanResponder时,可在onMoveShouldSetPanResponder中决定组件是否接管手势,并可配合InteractionManager.runAfterInteractions推迟耗时操作,避免转场动画掉帧[5]。 AsyncStorage提供异步持久化存储,适合在应用重启后保留数据,可以替代同步的localStorage,常与 Redux 集成做持久化[4]。- 路由通过导航组件以堆栈方式管理页面切换,类似 Activity 栈,比如调用
navigation.navigate('Details', { itemId: 86 })并可在目标页面通过route.params.itemId读取参数[8]。
本系列的 7 步学习地图
本系列共 7 篇,从概念到实战逐步覆盖 React Native 核心内容:
- React Native 概述(本篇)
- React Native 核心概念——组件、状态、生命周期与平台适配
- 样式与布局——Flexbox、StyleSheet 与响应式设计
- 导航与路由——堆栈、标签页与深层链接
- 原生模块与桥接——从 Bridge 到 Turbo Modules 的实践
- 性能优化——列表、图片、动画与包体积
- 测试与发布——单元测试、E2E、打包与商店上架
下一站:React Native 核心概念。
