
第一版 React Native 的目录可以先压成一条主线来记:
rust
Examples -> Libraries -> React -> packager
业务示例 JS 框架层 iOS 实现层 打包工具
Examples 是业务示例,Libraries 是暴露给 JS 的框架层,React(旧名 ReactKit)是 iOS Native 实现层,packager 负责把 JS 源码打成 bundle。下面逐个目录拆开看。
1. 根目录概览
源码主线只有四个目录,其余都是工程化辅助:
swift
源码主线
Examples / Libraries / React / packager
工程化辅助
IntegrationTests iOS 集成测试
docs facebook.github.io 文档源
website 文档站 Jekyll 源
react-native-cli CLI npm 包
lint lint 规则配置
cli.js 顶层 CLI 入口
init.sh 初始化脚手架脚本
linter.js lint 运行脚本
React.podspec iOS CocoaPods 集成
package.json JS 工程配置
.flowconfig Flow 静态类型
.travis.yml CI 配置
jestSupport Jest 测试支持
runXcodeTests.sh Xcode 测试脚本
| 名称 | 类型 | 初步判断 |
|---|---|---|
Examples |
目录 | 示例 App / 业务入口 |
Libraries |
目录 | JS 框架层 |
React |
目录 | iOS Native 实现层(旧称 ReactKit) |
packager |
目录 | JS 打包工具 |
IntegrationTests |
目录 | iOS 集成测试 |
docs |
目录 | 文档源 |
website |
目录 | 文档站源 |
react-native-cli |
目录 | CLI npm 包 |
lint |
目录 | lint 规则 |
jestSupport |
目录 | Jest 测试支持 |
cli.js |
文件 | 顶层 CLI 入口 |
init.sh |
文件 | 脚手架初始化脚本 |
linter.js |
文件 | lint 运行脚本 |
package.json |
文件 | JS 工程配置 |
React.podspec |
文件 | iOS CocoaPods 集成 |
runXcodeTests.sh |
文件 | 测试脚本 |
.flowconfig |
文件 | Flow 类型配置 |
.travis.yml |
文件 | CI 配置 |
.eslintrc |
文件 | ESLint 规则 |
阅读时:业务示例 Examples、JS 框架 Libraries、iOS 实现 React、工具与配置(packager / jestSupport / docs / website / CLI)。
2. 从 TicTacToe 看启动链路
入口选 Examples/TicTacToe,理由是它够小:一个井字棋,刚好能完整串起「Native 启动 → JS 渲染」而不被业务逻辑干扰。
它有两个入口文件:
| 入口 | 文件 | 作用 |
|---|---|---|
| iOS 入口 | Examples/TicTacToe/TicTacToe/main.m |
启动 iOS App,进入 AppDelegate |
| JS 入口 | Examples/TicTacToe/TicTacToeApp.js |
注册并渲染 RN 根组件 |
整条启动链路:
rust
main.m
-> AppDelegate.m
-> RCTRootView
-> 加载 JS bundle
-> 执行 TicTacToeApp.js
-> AppRegistry.registerComponent('TicTacToeApp', ...)
-> 渲染 TicTacToeApp
main.m 是标准 iOS 入口,它启动 AppDelegate:
objectivec
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
真正把 RN 接进来的是 AppDelegate.m:
ini
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.includeRequire.runModule.bundle"];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"TicTacToeApp"
launchOptions:launchOptions];
这里只有两个关键点:
-
jsCodeLocation 指向 packager / dev server 生成的 JS bundle。
-
moduleName:@"TicTacToeApp" 必须和 JS 里的注册名一致。
JS 侧对应的就是:
javascript
AppRegistry.registerComponent('TicTacToeApp', () => TicTacToeApp);
所以 TicTacToe 的意义不在井字棋本身,而是用最小例子把这条链串起来:
rust
Native 启动 -> 加载 JS bundle -> 执行 JS -> 找到注册的根组件 -> 渲染 RN 页面
3. Libraries 目录:暴露给 JS 的框架层
Libraries 是第一版 RN 给业务 JS 用的框架层,但它不是纯 JS 目录------Text、Image、Network 这几个目录同时装着 JS 和 Native 两侧的代码。
按能力分组看:
r
框架出口
Libraries/react-native
JS 应用入口
Libraries/AppRegistry <- Native 通过它进入业务 JS
组件能力
Libraries/Components / Libraries/Text / Libraries/Image
样式能力
Libraries/StyleSheet
通信能力
Libraries/BatchedBridge
JS 引擎初始化
Libraries/JavaScriptAppEngine <- 设置全局对象、JSTimers、错误处理
设备与交互
Libraries/Device / Libraries/Interaction
Libraries/Geolocation / Libraries/Vibration / Libraries/AppStateIOS
网络能力
Libraries/Fetch / Libraries/Network
(XMLHttpRequest.ios.js 在 Network/ 里,不是独立目录)
调试
Libraries/RCTWebSocketDebugger
动画
Libraries/Animation
React 自身
Libraries/vendor/react <- 第一版内联 React,不走 npm
工具
Libraries/Utilities / Libraries/Storage
JS 目录与 Native 实现的对应关系:
| 能力 | JS 目录 | 对应的 Native 目录 / 文件 |
|---|---|---|
| 框架出口 | Libraries/react-native |
多个 Native 模块 |
| JS 应用入口 | Libraries/AppRegistry |
React/Base/RCTRootView(Native 触发) |
| 通用组件 | Libraries/Components |
React/Views/RCTViewManager 等 |
| 文本组件 | Libraries/Text/Text.js |
Libraries/Text/(含 RCTText、RCTTextManager、RCTShadowText) |
| 图片组件 | Libraries/Image/Image.ios.js |
Libraries/Image/(含 RCTStaticImage、RCTNetworkImageView、RCTImageDownloader) |
| 输入框 | Libraries/Components(TextInput) |
React/Views/RCTTextField* |
| 样式 | Libraries/StyleSheet |
React/Layout/Layout.c |
| 通信 | Libraries/BatchedBridge |
React/Base/RCTBridge |
| 网络 | Libraries/Fetch + Libraries/Network |
Libraries/Network/RCTDataManager.m |
| JS 引擎初始化 | Libraries/JavaScriptAppEngine/Initialization |
React/Executors/RCTContextExecutor |
| 调试 | Libraries/RCTWebSocketDebugger |
React/Executors/RCTWebViewExecutor |
| React 自身 | Libraries/vendor/react |
--- |
4. React 目录(旧称 ReactKit):iOS Native 实现层
React 是 RN 在 iOS 上的全部 Native 实现。它的职责可以拆成五块:加载并执行 JS(Executors)、维护 Bridge(Base)、创建 Native View(Views)并用 UIManager 调度它们(Modules)、做 Flex 布局(Layout 里的 C 算法),以及通过 UIView+React 这个 Category 给所有 UIView 注入 RN 寻址能力。
子目录职责
markdown
Base
Native 侧基础设施。RCTRootView / RCTBridge 在这里,
还有 RCTBridgeModule / RCTJavaScriptExecutor 两个协议,
以及 RCTConvert / RCTLog / RCTAssert / RCTDevMenu / RCTRedBox 等。
Executors
JS 执行环境。
RCTContextExecutor <- JavaScriptCore,生产路径
RCTWebViewExecutor <- WebView,调试路径
Views
iOS 原生视图映射。
给所有 UIView 注入 reactTag / reactSubviews
是 Bridge 用 reactTag 寻址 Native 视图的物理基础
Modules
Native 能力模块。最关键的是 RCTUIManager
整个 Native UI 调度的总管
注意:UIManager 在 Modules,不在 Views
Layout
只有 Layout.h / Layout.c 两个文件
css-layout(Yoga 前身)的 C 实现
iOS 直接调用,是第一版 Flex 布局的物理来源
React.xcodeproj 里几个必读类
| 类 | 作用 |
|---|---|
RCTRootView |
根视图 |
RCTBridge |
Bridge 调度核心 |
RCTBridgeModule |
Native Module 协议(第五/七章 必读) |
RCTJavaScriptExecutor |
JS 执行器协议(第四章第 2 节必读) |
RCTContextExecutor |
JS 执行器 JSC 实现 |
RCTWebViewExecutor |
JS 执行器 WebView 实现(调试用) |
RCTUIManager |
Native UI 调度总管(在 Modules,不在 Views) |
RCTViewManager |
Native View 工厂基类 |
RCTShadowView |
影子树(在 Views,不在 Layout) |
RCTTouchHandler |
触摸事件 |
RCTEventDispatcher |
事件分发 |
RCTConvert |
props 类型转换 |
UIView+React |
给所有 UIView 注入 reactTag 的 Category |
RCTDataManager |
网络模块(实际在 Libraries/Network/,不在 React/Modules/) |
持有与调度关系
objectivec
RCTBridge ──持有──▶ RCTContextExecutor / RCTWebViewExecutor
RCTBridge ──持有──▶ RCTUIManager
RCTBridge ──持有──▶ RCTTouchHandler / RCTEventDispatcher
RCTUIManager ──调度──▶ RCTViewManager
RCTViewManager ──工厂──▶ RCTView / RCTShadowView
RCTRootView ──持有──▶ RCTBridge
RCTRootView ──持有──▶ RCTTouchHandler
RCTConvert ──被调用──▶ RCTViewManager(解析 props)
RCTShadowView ──调用 C 算法──▶ Layout.c
UIView+React ──注入──▶ 所有 RCTView 系列(Category,物理上修改 UIView 类)
5. packager 目录:把 JS 开发方式带进原生 App
packager 是 RN 能用 JS 写原生 App 的关键工具之一。它做的事只有一句话:
把 JS 源码处理成 iOS App 能请求、能执行的 bundle。
所以后面在 AppDelegate.m 里看到的这个地址,
arduino
http://localhost:8081/TicTacToeApp.bundle
不是凭空冒出来的------它就是向 packager / dev server 请求一个动态构建好的 bundle。开发期还能靠它做热重载和快速调试。这一段的细节留到第 7 篇的打包链路展开,这里只需要记住它在地图上的位置。